PXELINUX: Print an error message if the config file is not found
[syslinux.git] / pxelinux.asm
blobac986d3aa35b9850148d5161276231261288d8f4
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 (C) 1994-2006 H. Peter Anvin
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 1472
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
57 %define HAVE_IDLE 1 ; idle is not a noop
59 %macro RESET_IDLE 0
60 call reset_idle
61 %endmacro
62 %macro DO_IDLE 0
63 call check_for_arp
64 %endmacro
67 ; TFTP operation codes
69 TFTP_RRQ equ htons(1) ; Read request
70 TFTP_WRQ equ htons(2) ; Write request
71 TFTP_DATA equ htons(3) ; Data packet
72 TFTP_ACK equ htons(4) ; ACK packet
73 TFTP_ERROR equ htons(5) ; ERROR packet
74 TFTP_OACK equ htons(6) ; OACK packet
77 ; TFTP error codes
79 TFTP_EUNDEF equ htons(0) ; Unspecified error
80 TFTP_ENOTFOUND equ htons(1) ; File not found
81 TFTP_EACCESS equ htons(2) ; Access violation
82 TFTP_ENOSPACE equ htons(3) ; Disk full
83 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
84 TFTP_EBADID equ htons(5) ; Unknown transfer
85 TFTP_EEXISTS equ htons(6) ; File exists
86 TFTP_ENOUSER equ htons(7) ; No such user
87 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
90 ; The following structure is used for "virtual kernels"; i.e. LILO-style
91 ; option labels. The options we permit here are `kernel' and `append
92 ; Since there is no room in the bottom 64K for all of these, we
93 ; stick them at vk_seg:0000 and copy them down before we need them.
95 struc vkernel
96 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
97 vk_rname: resb FILENAME_MAX ; Real name
98 vk_ipappend: resb 1 ; "IPAPPEND" flag
99 resb 1 ; Pad
100 vk_appendlen: resw 1
101 alignb 4
102 vk_append: resb max_cmd_len+1 ; Command line
103 alignb 4
104 vk_end: equ $ ; Should be <= vk_size
105 endstruc
108 ; Segment assignments in the bottom 640K
109 ; 0000h - main code/data segment (and BIOS segment)
111 real_mode_seg equ 4000h
112 vk_seg equ 3000h ; Virtual kernels
113 xfer_buf_seg equ 2000h ; Bounce buffer for I/O to high mem
114 pktbuf_seg equ 1000h ; Packet buffers segments
115 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
118 ; BOOTP/DHCP packet pattern
120 struc bootp_t
121 bootp:
122 .opcode resb 1 ; BOOTP/DHCP "opcode"
123 .hardware resb 1 ; ARP hardware type
124 .hardlen resb 1 ; Hardware address length
125 .gatehops resb 1 ; Used by forwarders
126 .ident resd 1 ; Transaction ID
127 .seconds resw 1 ; Seconds elapsed
128 .flags resw 1 ; Broadcast flags
129 .cip resd 1 ; Client IP
130 .yip resd 1 ; "Your" IP
131 .sip resd 1 ; Next server IP
132 .gip resd 1 ; Relay agent IP
133 .macaddr resb 16 ; Client MAC address
134 .sname resb 64 ; Server name (optional)
135 .bootfile resb 128 ; Boot file name
136 .option_magic resd 1 ; Vendor option magic cookie
137 .options resb 1260 ; Vendor options
138 endstruc
140 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
143 ; TFTP connection data structure. Each one of these corresponds to a local
144 ; UDP port. The size of this structure must be a power of 2.
145 ; HBO = host byte order; NBO = network byte order
146 ; (*) = written by options negotiation code, must be dword sized
148 struc open_file_t
149 tftp_localport resw 1 ; Local port number (0 = not in use)
150 tftp_remoteport resw 1 ; Remote port number
151 tftp_remoteip resd 1 ; Remote IP address
152 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
153 tftp_filesize resd 1 ; Total file size(*)
154 tftp_blksize resd 1 ; Block size for this connection(*)
155 tftp_bytesleft resw 1 ; Unclaimed data bytes
156 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
157 tftp_dataptr resw 1 ; Pointer to available data
158 resw 2 ; Currently unusued
159 ; At end since it should not be zeroed on socked close
160 tftp_pktbuf resw 1 ; Packet buffer offset
161 endstruc
162 %ifndef DEPEND
163 %if (open_file_t_size & (open_file_t_size-1))
164 %error "open_file_t is not a power of 2"
165 %endif
166 %endif
168 ; ---------------------------------------------------------------------------
169 ; BEGIN CODE
170 ; ---------------------------------------------------------------------------
173 ; Memory below this point is reserved for the BIOS and the MBR
175 section .earlybss
176 trackbufsize equ 8192
177 trackbuf resb trackbufsize ; Track buffer goes here
178 getcbuf resb trackbufsize
179 ; ends at 4800h
181 ; Put some large buffers here, before RBFG_brainfuck,
182 ; where we can still carefully control the address
183 ; assignments...
185 alignb open_file_t_size
186 Files resb MAX_OPEN*open_file_t_size
188 alignb FILENAME_MAX
189 BootFile resb 256 ; Boot file from DHCP packet
190 ConfigServer resd 1 ; Null prefix for mangled config name
191 ConfigName resb 256-4 ; Configuration file from DHCP option
192 PathPrefix resb 256 ; Path prefix derived from boot file
193 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
194 IPOption resb 80 ; ip= option buffer
195 InitStack resd 1 ; Pointer to reset stack (SS:SP)
196 PXEStack resd 1 ; Saved stack during PXE call
198 ; Warning here: RBFG build 22 randomly overwrites memory location
199 ; [0x5680,0x576c), possibly more. It seems that it gets confused and
200 ; screws up the pointer to its own internal packet buffer and starts
201 ; writing a received ARP packet into low memory.
202 RBFG_brainfuck resb 0E00h
204 section .latebss
205 alignb 4
206 RebootTime resd 1 ; Reboot timeout, if set by option
207 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
208 APIVer resw 1 ; PXE API version found
209 IPOptionLen resw 1 ; Length of IPOption
210 IdleTimer resw 1 ; Time to check for ARP?
211 LocalBootType resw 1 ; Local boot return code
212 PktTimeout resw 1 ; Timeout for current packet
213 RealBaseMem resw 1 ; Amount of DOS memory after freeing
214 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
216 ; The relative position of these fields matter!
217 MACLen resb 1 ; MAC address len
218 MACType resb 1 ; MAC address type
219 MAC resb 16 ; Actual MAC address
220 BOOTIFStr resb 7 ; Space for "BOOTIF="
221 MACStr resb 3*17 ; MAC address as a string
224 ; PXE packets which don't need static initialization
226 alignb 4
227 pxe_unload_stack_pkt:
228 .status: resw 1 ; Status
229 .reserved: resw 10 ; Reserved
230 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
232 alignb 16
233 ; BOOTP/DHCP packet buffer
235 alignb 16
236 packet_buf resb 2048 ; Transfer packet
237 packet_buf_size equ $-packet_buf
240 ; Constants for the xfer_buf_seg
242 ; The xfer_buf_seg is also used to store message file buffers. We
243 ; need two trackbuffers (text and graphics), plus a work buffer
244 ; for the graphics decompressor.
246 xbs_textbuf equ 0 ; Also hard-coded, do not change
247 xbs_vgabuf equ trackbufsize
248 xbs_vgatmpbuf equ 2*trackbufsize
250 section .text
252 ; PXELINUX needs more BSS than the other derivatives;
253 ; therefore we relocate it from 7C00h on startup.
255 StackBuf equ $ ; Base of stack if we use our own
258 ; Primary entry point.
260 bootsec equ $
261 _start:
262 pushfd ; Paranoia... in case of return to PXE
263 pushad ; ... save as much state as possible
264 push ds
265 push es
266 push fs
267 push gs
269 xor ax,ax
270 mov ds,ax
271 mov es,ax
273 %ifndef DEPEND
274 %if TEXT_START != 0x7c00
275 ; This is uglier than it should be, but works around
276 ; some NASM 0.98.38 bugs.
277 mov di,section..bcopy32.start
278 add di,__bcopy_size-4
279 lea si,[di-(TEXT_START-7C00h)]
280 lea cx,[di-(TEXT_START-4)]
281 shr cx,2
282 std ; Overlapping areas, copy backwards
283 rep movsd
284 %endif
285 %endif
286 jmp 0:_start1 ; Canonicalize address
287 _start1:
288 mov bp,sp
289 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
291 ; That is all pushed onto the PXE stack. Save the pointer
292 ; to it and switch to an internal stack.
293 mov [InitStack],sp
294 mov [InitStack+2],ss
296 %if USE_PXE_PROVIDED_STACK
297 ; Apparently some platforms go bonkers if we
298 ; set up our own stack...
299 mov [BaseStack],sp
300 mov [BaseStack+4],ss
301 %endif
303 cli ; Paranoia
304 lss esp,[BaseStack]
306 sti ; Stack set up and ready
307 cld ; Copy upwards
310 ; Initialize screen (if we're using one)
312 ; Now set up screen parameters
313 call adjust_screen
315 ; Wipe the F-key area
316 mov al,NULLFILE
317 mov di,FKeyName
318 mov cx,10*(1 << FILENAME_MAX_LG2)
319 push es ; Save ES -> PXE structure
320 push ds ; ES <- DS
321 pop es
322 rep stosb
323 pop es ; Restore ES
326 ; Tell the user we got this far
328 mov si,syslinux_banner
329 call writestr
331 mov si,copyright_str
332 call writestr
335 ; Assume API version 2.1, in case we find the !PXE structure without
336 ; finding the PXENV+ structure. This should really look at the Base
337 ; Code ROM ID structure in have_pxe, but this is adequate for now --
338 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
339 ; about higher versions than that.
341 mov word [APIVer],0201h
344 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
345 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
346 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
347 ; We should make that the second test, and not trash ES:BX...
349 cmp dword [es:bx], '!PXE'
350 je have_pxe
352 ; Uh-oh, not there... try plan B
353 mov ax, 5650h
354 %if USE_PXE_PROVIDED_STACK == 0
355 lss sp,[InitStack]
356 %endif
357 int 1Ah ; May trash regs
358 %if USE_PXE_PROVIDED_STACK == 0
359 lss esp,[BaseStack]
360 %endif
362 jc no_pxe
363 cmp ax,564Eh
364 jne no_pxe
366 ; Okay, that gave us the PXENV+ structure, find !PXE
367 ; structure from that (if available)
368 cmp dword [es:bx], 'PXEN'
369 jne no_pxe
370 cmp word [es:bx+4], 'V+'
371 je have_pxenv
373 ; Nothing there either. Last-ditch: scan memory
374 call memory_scan_for_pxe_struct ; !PXE scan
375 jnc have_pxe
376 call memory_scan_for_pxenv_struct ; PXENV+ scan
377 jnc have_pxenv
379 no_pxe: mov si,err_nopxe
380 call writestr
381 jmp kaboom
383 have_pxenv:
384 mov [StrucPtr],bx
385 mov [StrucPtr+2],es
387 mov si,found_pxenv
388 call writestr
390 mov si,apiver_str
391 call writestr
392 mov ax,[es:bx+6]
393 mov [APIVer],ax
394 call writehex4
395 call crlf
397 cmp ax,0201h ; API version 2.1 or higher
398 jb old_api
399 mov si,bx
400 mov ax,es
401 les bx,[es:bx+28h] ; !PXE structure pointer
402 cmp dword [es:bx],'!PXE'
403 je have_pxe
405 ; Nope, !PXE structure missing despite API 2.1+, or at least
406 ; the pointer is missing. Do a last-ditch attempt to find it.
407 call memory_scan_for_pxe_struct
408 jnc have_pxe
410 ; Otherwise, no dice, use PXENV+ structure
411 mov bx,si
412 mov es,ax
414 old_api: ; Need to use a PXENV+ structure
415 mov si,using_pxenv_msg
416 call writestr
418 mov eax,[es:bx+0Ah] ; PXE RM API
419 mov [PXENVEntry],eax
421 mov si,undi_data_msg
422 call writestr
423 mov ax,[es:bx+20h]
424 call writehex4
425 call crlf
426 mov si,undi_data_len_msg
427 call writestr
428 mov ax,[es:bx+22h]
429 call writehex4
430 call crlf
431 mov si,undi_code_msg
432 call writestr
433 mov ax,[es:bx+24h]
434 call writehex4
435 call crlf
436 mov si,undi_code_len_msg
437 call writestr
438 mov ax,[es:bx+26h]
439 call writehex4
440 call crlf
442 ; Compute base memory size from PXENV+ structure
443 xor esi,esi
444 movzx eax,word [es:bx+20h] ; UNDI data seg
445 cmp ax,[es:bx+24h] ; UNDI code seg
446 ja .use_data
447 mov ax,[es:bx+24h]
448 mov si,[es:bx+26h]
449 jmp short .combine
450 .use_data:
451 mov si,[es:bx+22h]
452 .combine:
453 shl eax,4
454 add eax,esi
455 shr eax,10 ; Convert to kilobytes
456 mov [RealBaseMem],ax
458 mov si,pxenventry_msg
459 call writestr
460 mov ax,[PXENVEntry+2]
461 call writehex4
462 mov al,':'
463 call writechr
464 mov ax,[PXENVEntry]
465 call writehex4
466 call crlf
467 jmp have_entrypoint
469 have_pxe:
470 mov [StrucPtr],bx
471 mov [StrucPtr+2],es
473 mov eax,[es:bx+10h]
474 mov [PXEEntry],eax
476 mov si,undi_data_msg
477 call writestr
478 mov eax,[es:bx+2Ah]
479 call writehex8
480 call crlf
481 mov si,undi_data_len_msg
482 call writestr
483 mov ax,[es:bx+2Eh]
484 call writehex4
485 call crlf
486 mov si,undi_code_msg
487 call writestr
488 mov ax,[es:bx+32h]
489 call writehex8
490 call crlf
491 mov si,undi_code_len_msg
492 call writestr
493 mov ax,[es:bx+36h]
494 call writehex4
495 call crlf
497 ; Compute base memory size from !PXE structure
498 xor esi,esi
499 mov eax,[es:bx+2Ah]
500 cmp eax,[es:bx+32h]
501 ja .use_data
502 mov eax,[es:bx+32h]
503 mov si,[es:bx+36h]
504 jmp short .combine
505 .use_data:
506 mov si,[es:bx+2Eh]
507 .combine:
508 add eax,esi
509 shr eax,10
510 mov [RealBaseMem],ax
512 mov si,pxeentry_msg
513 call writestr
514 mov ax,[PXEEntry+2]
515 call writehex4
516 mov al,':'
517 call writechr
518 mov ax,[PXEEntry]
519 call writehex4
520 call crlf
522 have_entrypoint:
523 push cs
524 pop es ; Restore CS == DS == ES
527 ; Network-specific initialization
529 xor ax,ax
530 mov [LocalDomain],al ; No LocalDomain received
533 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
534 ; address). This lives in the DHCPACK packet (query info 2).
536 query_bootp:
537 mov di,pxe_bootp_query_pkt_2
538 mov bx,PXENV_GET_CACHED_INFO
540 call pxenv
541 push word [pxe_bootp_query_pkt_2.status]
542 jc .pxe_err1
543 cmp ax,byte 0
544 je .pxe_ok
545 .pxe_err1:
546 mov di,pxe_bootp_size_query_pkt
547 mov bx,PXENV_GET_CACHED_INFO
549 call pxenv
550 jc .pxe_err
551 .pxe_size:
552 mov ax,[pxe_bootp_size_query_pkt.buffersize]
553 call writehex4
554 call crlf
556 .pxe_err:
557 mov si,err_pxefailed
558 call writestr
559 call writehex4
560 mov al, ' '
561 call writechr
562 pop ax ; Status
563 call writehex4
564 call crlf
565 jmp kaboom ; We're dead
567 .pxe_ok:
568 pop cx ; Forget status
569 mov cx,[pxe_bootp_query_pkt_2.buffersize]
570 call parse_dhcp ; Parse DHCP packet
572 ; Save away MAC address (assume this is in query info 2. If this
573 ; turns out to be problematic it might be better getting it from
574 ; the query info 1 packet.)
576 .save_mac:
577 movzx cx,byte [trackbuf+bootp.hardlen]
578 mov [MACLen],cl
579 mov al,[trackbuf+bootp.hardware]
580 mov [MACType],al
581 mov si,trackbuf+bootp.macaddr
582 mov di,MAC
583 push cx
584 rep movsb
585 mov cx,MAC+16
586 sub cx,di
587 xor ax,ax
588 rep stosb
590 mov si,bootif_str
591 mov di,BOOTIFStr
592 mov cx,bootif_str_len
593 rep movsb
595 pop cx
596 mov si,MACType
597 inc cx
598 mov bx,hextbl_lower
599 .hexify_mac:
600 lodsb
601 mov ah,al
602 shr al,4
603 xlatb
604 stosb
605 mov al,ah
606 and al,0Fh
607 xlatb
608 stosb
609 mov al,'-'
610 stosb
611 loop .hexify_mac
612 mov [di-1],byte 0 ; Null-terminate and strip final colon
615 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
616 ; packet (query info 3).
618 mov [pxe_bootp_size_query_pkt.packettype], byte 3
620 mov di,pxe_bootp_query_pkt_3
621 mov bx,PXENV_GET_CACHED_INFO
623 call pxenv
624 push word [pxe_bootp_query_pkt_3.status]
625 jc .pxe_err1
626 cmp ax,byte 0
627 jne .pxe_err1
629 ; Packet loaded OK...
630 pop cx ; Forget status
631 mov cx,[pxe_bootp_query_pkt_3.buffersize]
632 call parse_dhcp ; Parse DHCP packet
634 ; Generate ip= option
636 call genipopt
639 ; Print IP address
641 mov eax,[MyIP]
642 mov di,DotQuadBuf
643 push di
644 call gendotquad ; This takes network byte order input
646 xchg ah,al ; Convert to host byte order
647 ror eax,16 ; (BSWAP doesn't work on 386)
648 xchg ah,al
650 mov si,myipaddr_msg
651 call writestr
652 call writehex8
653 mov al,' '
654 call writechr
655 pop si ; DotQuadBuf
656 call writestr
657 call crlf
659 mov si,IPOption
660 call writestr
661 call crlf
664 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
665 ; if we didn't get the magic enable, do not recognize any other options.
667 check_dhcp_magic:
668 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
669 jnz .got_magic
670 mov byte [DHCPMagic], 0 ; If not, kill all other options
671 .got_magic:
675 ; Initialize UDP stack
677 udp_init:
678 mov eax,[MyIP]
679 mov [pxe_udp_open_pkt.sip],eax
680 mov di,pxe_udp_open_pkt
681 mov bx,PXENV_UDP_OPEN
682 call pxenv
683 jc .failed
684 cmp word [pxe_udp_open_pkt.status], byte 0
685 je .success
686 .failed: mov si,err_udpinit
687 call writestr
688 jmp kaboom
689 .success:
692 ; Common initialization code
694 %include "init.inc"
695 %include "cpuinit.inc"
698 ; Now we're all set to start with our *real* business. First load the
699 ; configuration file (if any) and parse it.
701 ; In previous versions I avoided using 32-bit registers because of a
702 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
703 ; random. I figure, though, that if there are any of those still left
704 ; they probably won't be trying to install Linux on them...
706 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
707 ; to take'm out. In fact, we may want to put them back if we're going
708 ; to boot ELKS at some point.
712 ; Store standard filename prefix
714 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
715 jnz .got_prefix
716 mov si,BootFile
717 mov di,PathPrefix
719 call strcpy
720 mov cx,di
721 sub cx,PathPrefix+1
723 lea si,[di-2] ; Skip final null!
724 .find_alnum: lodsb
725 or al,20h
726 cmp al,'.' ; Count . or - as alphanum
727 je .alnum
728 cmp al,'-'
729 je .alnum
730 cmp al,'0'
731 jb .notalnum
732 cmp al,'9'
733 jbe .alnum
734 cmp al,'a'
735 jb .notalnum
736 cmp al,'z'
737 ja .notalnum
738 .alnum: loop .find_alnum
739 dec si
740 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
742 .got_prefix:
743 mov si,tftpprefix_msg
744 call writestr
745 mov si,PathPrefix
746 call writestr
747 call crlf
750 ; Load configuration file
752 find_config:
755 ; Begin looking for configuration file
757 config_scan:
758 mov di,ConfigServer
759 xor eax,eax
760 stosd ; The config file is always from the server
762 test byte [DHCPMagic], 02h
763 jz .no_option
765 ; We got a DHCP option, try it first
766 mov si,trying_msg
767 call writestr
768 ; mov di,ConfigName ; - already the case
769 mov si,di
770 call writestr
771 call crlf
772 mov di,ConfigServer
773 call open
774 jnz .success
776 .no_option:
777 mov di,ConfigName
778 mov si,cfgprefix
779 mov cx,cfgprefix_len
780 rep movsb
782 ; Try loading by MAC address
783 ; Have to guess config file name
784 push di
785 mov si,MACStr
786 mov cx,(3*17+1)/2
787 rep movsw
788 mov si,trying_msg
789 call writestr
790 mov di,ConfigName
791 mov si,di
792 call writestr
793 call crlf
794 mov di,ConfigServer
795 call open
796 pop di
797 jnz .success
799 .scan_ip:
800 mov cx,8
801 mov eax,[MyIP]
802 xchg ah,al ; Convert to host byte order
803 ror eax,16
804 xchg ah,al
805 .hexify_loop: rol eax,4
806 push eax
807 and al,0Fh
808 cmp al,10
809 jae .high
810 .low: add al,'0'
811 jmp short .char
812 .high: add al,'A'-10
813 .char: stosb
814 pop eax
815 loop .hexify_loop
817 mov cx,9 ; Up to 9 attempts
819 .tryagain: mov byte [di],0
820 cmp cx,byte 1
821 jne .not_default
822 pusha
823 mov si,default_str
824 mov cx,default_len
825 rep movsb ; Copy "default" string
826 popa
827 .not_default: pusha
828 mov si,trying_msg
829 call writestr
830 mov di,ConfigName
831 mov si,di
832 call writestr
833 call crlf
834 mov di,ConfigServer
835 call open
836 popa
837 jnz .success
838 dec di
839 loop .tryagain
841 mov si,err_noconfig
842 call writestr
843 jmp kaboom
845 .success:
848 ; Now we have the config file open. Parse the config file and
849 ; run the user interface.
851 %include "ui.inc"
854 ; Linux kernel loading code is common. However, we need to define
855 ; a couple of helper macros...
858 ; Handle "ipappend" option
859 %define HAVE_SPECIAL_APPEND
860 %macro SPECIAL_APPEND 0
861 test byte [IPAppend],01h ; ip=
862 jz .noipappend1
863 mov si,IPOption
864 mov cx,[IPOptionLen]
865 rep movsb
866 mov al,' '
867 stosb
868 .noipappend1:
869 test byte [IPAppend],02h
870 jz .noipappend2
871 mov si,BOOTIFStr
872 call strcpy
873 mov byte [es:di-1],' ' ; Replace null with space
874 .noipappend2:
875 %endmacro
877 ; Unload PXE stack
878 %define HAVE_UNLOAD_PREP
879 %macro UNLOAD_PREP 0
880 call unload_pxe
881 %endmacro
883 %include "runkernel.inc"
886 ; COMBOOT-loading code
888 %include "comboot.inc"
889 %include "com32.inc"
890 %include "cmdline.inc"
893 ; Boot sector loading code
895 %include "bootsect.inc"
898 ; Boot to the local disk by returning the appropriate PXE magic.
899 ; AX contains the appropriate return code.
901 local_boot:
902 push cs
903 pop ds
904 mov [LocalBootType],ax
905 call vgaclearmode
906 mov si,localboot_msg
907 call writestr
908 ; Restore the environment we were called with
909 lss sp,[InitStack]
910 pop gs
911 pop fs
912 pop es
913 pop ds
914 popad
915 mov ax,[cs:LocalBootType]
916 popfd
917 retf ; Return to PXE
920 ; Abort loading code
922 %include "abort.inc"
925 ; kaboom: write a message and bail out. Wait for quite a while,
926 ; or a user keypress, then do a hard reboot.
928 kaboom:
929 RESET_STACK_AND_SEGS AX
930 .patch: mov si,bailmsg
931 call writestr ; Returns with AL = 0
932 .drain: call pollchar
933 jz .drained
934 call getchar
935 jmp short .drain
936 .drained:
937 mov edi,[RebootTime]
938 mov al,[DHCPMagic]
939 and al,09h ; Magic+Timeout
940 cmp al,09h
941 je .time_set
942 mov edi,REBOOT_TIME
943 .time_set:
944 mov cx,18
945 .wait1: push cx
946 mov ecx,edi
947 .wait2: mov dx,[BIOS_timer]
948 .wait3: call pollchar
949 jnz .keypress
950 cmp dx,[BIOS_timer]
951 je .wait3
952 loop .wait2,ecx
953 mov al,'.'
954 call writechr
955 pop cx
956 loop .wait1
957 .keypress:
958 call crlf
959 mov word [BIOS_magic],0 ; Cold reboot
960 jmp 0F000h:0FFF0h ; Reset vector address
963 ; memory_scan_for_pxe_struct:
965 ; If none of the standard methods find the !PXE structure, look for it
966 ; by scanning memory.
968 ; On exit, if found:
969 ; CF = 0, ES:BX -> !PXE structure
970 ; Otherwise CF = 1, all registers saved
972 memory_scan_for_pxe_struct:
973 push ds
974 pusha
975 mov ax,cs
976 mov ds,ax
977 mov si,trymempxe_msg
978 call writestr
979 mov ax,[BIOS_fbm] ; Starting segment
980 shl ax,(10-4) ; Kilobytes -> paragraphs
981 ; mov ax,01000h ; Start to look here
982 dec ax ; To skip inc ax
983 .mismatch:
984 inc ax
985 cmp ax,0A000h ; End of memory
986 jae .not_found
987 call writehex4
988 mov si,fourbs_msg
989 call writestr
990 mov es,ax
991 mov edx,[es:0]
992 cmp edx,'!PXE'
993 jne .mismatch
994 movzx cx,byte [es:4] ; Length of structure
995 cmp cl,08h ; Minimum length
996 jb .mismatch
997 push ax
998 xor ax,ax
999 xor si,si
1000 .checksum: es lodsb
1001 add ah,al
1002 loop .checksum
1003 pop ax
1004 jnz .mismatch ; Checksum must == 0
1005 .found: mov bp,sp
1006 xor bx,bx
1007 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
1008 mov ax,es
1009 call writehex4
1010 call crlf
1011 popa
1012 pop ds
1015 .not_found: mov si,notfound_msg
1016 call writestr
1017 popa
1018 pop ds
1023 ; memory_scan_for_pxenv_struct:
1025 ; If none of the standard methods find the PXENV+ structure, look for it
1026 ; by scanning memory.
1028 ; On exit, if found:
1029 ; CF = 0, ES:BX -> PXENV+ structure
1030 ; Otherwise CF = 1, all registers saved
1032 memory_scan_for_pxenv_struct:
1033 pusha
1034 mov si,trymempxenv_msg
1035 call writestr
1036 ; mov ax,[BIOS_fbm] ; Starting segment
1037 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1038 mov ax,01000h ; Start to look here
1039 dec ax ; To skip inc ax
1040 .mismatch:
1041 inc ax
1042 cmp ax,0A000h ; End of memory
1043 jae .not_found
1044 mov es,ax
1045 mov edx,[es:0]
1046 cmp edx,'PXEN'
1047 jne .mismatch
1048 mov dx,[es:4]
1049 cmp dx,'V+'
1050 jne .mismatch
1051 movzx cx,byte [es:8] ; Length of structure
1052 cmp cl,26h ; Minimum length
1053 jb .mismatch
1054 xor ax,ax
1055 xor si,si
1056 .checksum: es lodsb
1057 add ah,al
1058 loop .checksum
1059 and ah,ah
1060 jnz .mismatch ; Checksum must == 0
1061 .found: mov bp,sp
1062 mov [bp+8],bx ; Save BX into stack frame
1063 mov ax,bx
1064 call writehex4
1065 call crlf
1068 .not_found: mov si,notfound_msg
1069 call writestr
1070 popad
1075 ; searchdir:
1077 ; Open a TFTP connection to the server
1079 ; On entry:
1080 ; DS:DI = mangled filename
1081 ; If successful:
1082 ; ZF clear
1083 ; SI = socket pointer
1084 ; DX:AX = file length in bytes
1085 ; If unsuccessful
1086 ; ZF set
1089 searchdir:
1090 push es
1091 push bx
1092 push cx
1093 mov ax,ds
1094 mov es,ax
1095 mov si,di
1096 push bp
1097 mov bp,sp
1099 call allocate_socket
1100 jz .ret
1102 mov ax,PKT_RETRY ; Retry counter
1103 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1105 .sendreq: push ax ; [bp-2] - Retry counter
1106 push si ; [bp-4] - File name
1108 mov di,packet_buf
1109 mov [pxe_udp_write_pkt.buffer],di
1111 mov ax,TFTP_RRQ ; TFTP opcode
1112 stosw
1114 lodsd ; EAX <- server override (if any)
1115 and eax,eax
1116 jnz .noprefix ; No prefix, and we have the server
1118 push si ; Add common prefix
1119 mov si,PathPrefix
1120 call strcpy
1121 dec di
1122 pop si
1124 mov eax,[ServerIP] ; Get default server
1126 .noprefix:
1127 call strcpy ; Filename
1129 mov [bx+tftp_remoteip],eax
1131 push bx ; [bp-6] - TFTP block
1132 mov bx,[bx]
1133 push bx ; [bp-8] - TID (local port no)
1135 mov [pxe_udp_write_pkt.status],byte 0
1136 mov [pxe_udp_write_pkt.sip],eax
1137 ; Now figure out the gateway
1138 xor eax,[MyIP]
1139 and eax,[Netmask]
1140 jz .nogwneeded
1141 mov eax,[Gateway]
1142 .nogwneeded:
1143 mov [pxe_udp_write_pkt.gip],eax
1144 mov [pxe_udp_write_pkt.lport],bx
1145 mov ax,[ServerPort]
1146 mov [pxe_udp_write_pkt.rport],ax
1147 mov si,tftp_tail
1148 mov cx,tftp_tail_len
1149 rep movsb
1150 sub di,packet_buf ; Get packet size
1151 mov [pxe_udp_write_pkt.buffersize],di
1153 mov di,pxe_udp_write_pkt
1154 mov bx,PXENV_UDP_WRITE
1155 call pxenv
1156 jc .failure
1157 cmp word [pxe_udp_write_pkt.status],byte 0
1158 jne .failure
1161 ; Danger, Will Robinson! We need to support timeout
1162 ; and retry lest we just lost a packet...
1165 ; Packet transmitted OK, now we need to receive
1166 .getpacket: push word [PktTimeout] ; [bp-10]
1167 push word [BIOS_timer] ; [bp-12]
1169 .pkt_loop: mov bx,[bp-8] ; TID
1170 mov di,packet_buf
1171 mov word [pxe_udp_read_pkt.status],0
1172 mov [pxe_udp_read_pkt.buffer],di
1173 mov [pxe_udp_read_pkt.buffer+2],ds
1174 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1175 mov eax,[MyIP]
1176 mov [pxe_udp_read_pkt.dip],eax
1177 mov [pxe_udp_read_pkt.lport],bx
1178 mov di,pxe_udp_read_pkt
1179 mov bx,PXENV_UDP_READ
1180 call pxenv
1181 and ax,ax
1182 jz .got_packet ; Wait for packet
1183 .no_packet:
1184 mov dx,[BIOS_timer]
1185 cmp dx,[bp-12]
1186 je .pkt_loop
1187 mov [bp-12],dx
1188 dec word [bp-10] ; Timeout
1189 jnz .pkt_loop
1190 pop ax ; Adjust stack
1191 pop ax
1192 shl word [PktTimeout],1 ; Exponential backoff
1193 jmp .failure
1195 .got_packet:
1196 mov si,[bp-6] ; TFTP pointer
1197 mov bx,[bp-8] ; TID
1199 mov eax,[si+tftp_remoteip]
1200 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1201 jne .no_packet
1203 ; Got packet - reset timeout
1204 mov word [PktTimeout],PKT_TIMEOUT
1206 pop ax ; Adjust stack
1207 pop ax
1209 mov ax,[pxe_udp_read_pkt.rport]
1210 mov [si+tftp_remoteport],ax
1212 ; filesize <- -1 == unknown
1213 mov dword [si+tftp_filesize], -1
1214 ; Default blksize unless blksize option negotiated
1215 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1217 mov cx,[pxe_udp_read_pkt.buffersize]
1218 sub cx,2 ; CX <- bytes after opcode
1219 jb .failure ; Garbled reply
1221 mov si,packet_buf
1222 lodsw
1224 cmp ax, TFTP_ERROR
1225 je .bailnow ; ERROR reply: don't try again
1227 cmp ax, TFTP_OACK
1228 jne .no_tsize
1230 ; Now we need to parse the OACK packet to get the transfer
1231 ; size. SI -> first byte of options; CX -> byte count
1232 .parse_oack:
1233 jcxz .no_tsize ; No options acked
1234 .get_opt_name:
1235 mov di,si
1236 mov bx,si
1237 .opt_name_loop: lodsb
1238 and al,al
1239 jz .got_opt_name
1240 or al,20h ; Convert to lowercase
1241 stosb
1242 loop .opt_name_loop
1243 ; We ran out, and no final null
1244 jmp .err_reply
1245 .got_opt_name: ; si -> option value
1246 dec cx ; bytes left in pkt
1247 jz .err_reply ; Option w/o value
1249 ; Parse option pointed to by bx; guaranteed to be
1250 ; null-terminated.
1251 push cx
1252 push si
1253 mov si,bx ; -> option name
1254 mov bx,tftp_opt_table
1255 mov cx,tftp_opts
1256 .opt_loop:
1257 push cx
1258 push si
1259 mov di,[bx] ; Option pointer
1260 mov cx,[bx+2] ; Option len
1261 repe cmpsb
1262 pop si
1263 pop cx
1264 je .get_value ; OK, known option
1265 add bx,6
1266 loop .opt_loop
1268 pop si
1269 pop cx
1270 jmp .err_reply ; Non-negotiated option returned
1272 .get_value: pop si ; si -> option value
1273 pop cx ; cx -> bytes left in pkt
1274 mov bx,[bx+4] ; Pointer to data target
1275 add bx,[bp-6] ; TFTP socket pointer
1276 xor eax,eax
1277 xor edx,edx
1278 .value_loop: lodsb
1279 and al,al
1280 jz .got_value
1281 sub al,'0'
1282 cmp al, 9
1283 ja .err_reply ; Not a decimal digit
1284 imul edx,10
1285 add edx,eax
1286 mov [bx],edx
1287 loop .value_loop
1288 ; Ran out before final null, accept anyway
1289 jmp short .done_pkt
1291 .got_value:
1292 dec cx
1293 jnz .get_opt_name ; Not end of packet
1295 ; ZF == 1
1297 ; Success, done!
1298 .done_pkt:
1299 pop si ; Junk
1300 pop si ; We want the packet ptr in SI
1302 mov eax,[si+tftp_filesize]
1303 cmp eax,-1
1304 jz .no_tsize
1305 mov edx,eax
1306 shr edx,16 ; DX:AX == EAX
1308 and eax,eax ; Set ZF depending on file size
1309 pop bp ; Junk
1310 pop bp ; Junk (retry counter)
1311 jz .error_si ; ZF = 1 need to free the socket
1312 .ret:
1313 pop bp
1314 pop cx
1315 pop bx
1316 pop es
1319 .no_tsize:
1320 .err_reply: ; Option negotiation error. Send ERROR reply.
1321 ; ServerIP and gateway are already programmed in
1322 mov si,[bp-6]
1323 mov ax,[si+tftp_remoteport]
1324 mov word [pxe_udp_write_pkt.rport],ax
1325 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1326 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1327 mov di,pxe_udp_write_pkt
1328 mov bx,PXENV_UDP_WRITE
1329 call pxenv
1331 ; Write an error message and explode
1332 mov si,err_oldtftp
1333 call writestr
1334 jmp kaboom
1336 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1338 .failure: pop bx ; Junk
1339 pop bx
1340 pop si
1341 pop ax
1342 dec ax ; Retry counter
1343 jnz .sendreq ; Try again
1345 .error: mov si,bx ; Socket pointer
1346 .error_si: ; Socket pointer already in SI
1347 call free_socket ; ZF <- 1, SI <- 0
1348 jmp .ret
1351 ; allocate_socket: Allocate a local UDP port structure
1353 ; If successful:
1354 ; ZF set
1355 ; BX = socket pointer
1356 ; If unsuccessful:
1357 ; ZF clear
1359 allocate_socket:
1360 push cx
1361 mov bx,Files
1362 mov cx,MAX_OPEN
1363 .check: cmp word [bx], byte 0
1364 je .found
1365 add bx,open_file_t_size
1366 loop .check
1367 xor cx,cx ; ZF = 1
1368 pop cx
1370 ; Allocate a socket number. Socket numbers are made
1371 ; guaranteed unique by including the socket slot number
1372 ; (inverted, because we use the loop counter cx); add a
1373 ; counter value to keep the numbers from being likely to
1374 ; get immediately reused.
1376 ; The NextSocket variable also contains the top two bits
1377 ; set. This generates a value in the range 49152 to
1378 ; 57343.
1379 .found:
1380 dec cx
1381 push ax
1382 mov ax,[NextSocket]
1383 inc ax
1384 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1385 mov [NextSocket],ax
1386 shl cx,13-MAX_OPEN_LG2
1387 add cx,ax ; ZF = 0
1388 xchg ch,cl ; Convert to network byte order
1389 mov [bx],cx ; Socket in use
1390 pop ax
1391 pop cx
1395 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1397 free_socket:
1398 push es
1399 pusha
1400 xor ax,ax
1401 mov es,ax
1402 mov di,si
1403 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1404 rep stosw
1405 popa
1406 pop es
1407 xor si,si
1411 ; parse_dotquad:
1412 ; Read a dot-quad pathname in DS:SI and output an IP
1413 ; address in EAX, with SI pointing to the first
1414 ; nonmatching character.
1416 ; Return CF=1 on error.
1418 parse_dotquad:
1419 push cx
1420 mov cx,4
1421 xor eax,eax
1422 .parseloop:
1423 mov ch,ah
1424 mov ah,al
1425 lodsb
1426 sub al,'0'
1427 jb .notnumeric
1428 cmp al,9
1429 ja .notnumeric
1430 aad ; AL += 10 * AH; AH = 0;
1431 xchg ah,ch
1432 jmp .parseloop
1433 .notnumeric:
1434 cmp al,'.'-'0'
1435 pushf
1436 mov al,ah
1437 mov ah,ch
1438 xor ch,ch
1439 ror eax,8
1440 popf
1441 jne .error
1442 loop .parseloop
1443 jmp .done
1444 .error:
1445 loop .realerror ; If CX := 1 then we're done
1447 jmp .done
1448 .realerror:
1450 .done:
1451 dec si ; CF unchanged!
1452 pop cx
1455 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1456 ; to by ES:DI; ends on encountering any whitespace.
1458 ; This verifies that a filename is < FILENAME_MAX characters
1459 ; and doesn't contain whitespace, and zero-pads the output buffer,
1460 ; so "repe cmpsb" can do a compare.
1462 ; The first four bytes of the manged name is the IP address of
1463 ; the download host.
1465 mangle_name:
1466 push si
1467 mov eax,[ServerIP]
1468 cmp byte [si],0
1469 je .noip ; Null filename?!?!
1470 cmp word [si],'::' ; Leading ::?
1471 je .gotprefix
1473 .more:
1474 inc si
1475 cmp byte [si],0
1476 je .noip
1477 cmp word [si],'::'
1478 jne .more
1480 ; We have a :: prefix of some sort, it could be either
1481 ; a DNS name or a dot-quad IP address. Try the dot-quad
1482 ; first...
1483 .here:
1484 pop si
1485 push si
1486 call parse_dotquad
1487 jc .notdq
1488 cmp word [si],'::'
1489 je .gotprefix
1490 .notdq:
1491 pop si
1492 push si
1493 call dns_resolv
1494 cmp word [si],'::'
1495 jne .noip
1496 and eax,eax
1497 jnz .gotprefix
1499 .noip:
1500 pop si
1501 xor eax,eax
1502 jmp .prefix_done
1504 .gotprefix:
1505 pop cx ; Adjust stack
1506 inc si ; Skip double colon
1507 inc si
1509 .prefix_done:
1510 stosd ; Save IP address prefix
1511 mov cx,FILENAME_MAX-5
1513 .mn_loop:
1514 lodsb
1515 cmp al,' ' ; If control or space, end
1516 jna .mn_end
1517 stosb
1518 loop .mn_loop
1519 .mn_end:
1520 inc cx ; At least one null byte
1521 xor ax,ax ; Zero-fill name
1522 rep stosb ; Doesn't do anything if CX=0
1523 ret ; Done
1526 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1527 ; filename to the conventional representation. This is needed
1528 ; for the BOOT_IMAGE= parameter for the kernel.
1529 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1530 ; known to be shorter.
1532 ; DS:SI -> input mangled file name
1533 ; ES:DI -> output buffer
1535 ; On return, DI points to the first byte after the output name,
1536 ; which is set to a null byte.
1538 unmangle_name:
1539 push eax
1540 lodsd
1541 and eax,eax
1542 jz .noip
1543 call gendotquad
1544 mov ax,'::'
1545 stosw
1546 .noip:
1547 call strcpy
1548 dec di ; Point to final null byte
1549 pop eax
1553 ; pxenv
1555 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1556 ; calling convention. This is a separate local routine so
1557 ; we can hook special things from it if necessary. In particular,
1558 ; some PXE stacks seem to not like being invoked from anything but
1559 ; the initial stack, so humour it.
1562 pxenv:
1563 %if USE_PXE_PROVIDED_STACK == 0
1564 mov [cs:PXEStack],sp
1565 mov [cs:PXEStack+2],ss
1566 lss sp,[cs:InitStack]
1567 %endif
1568 .jump: call 0:pxe_thunk ; Default to calling the thunk
1569 %if USE_PXE_PROVIDED_STACK == 0
1570 lss sp,[cs:PXEStack]
1571 %endif
1572 cld ; Make sure DF <- 0
1575 ; Must be after function def due to NASM bug
1576 PXENVEntry equ pxenv.jump+1
1579 ; pxe_thunk
1581 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1582 ; calling convention (using the stack.)
1584 ; This is called as a far routine so that we can just stick it into
1585 ; the PXENVEntry variable.
1587 pxe_thunk: push es
1588 push di
1589 push bx
1590 .jump: call 0:0
1591 add sp,byte 6
1592 cmp ax,byte 1
1593 cmc ; Set CF unless ax == 0
1594 retf
1596 ; Must be after function def due to NASM bug
1597 PXEEntry equ pxe_thunk.jump+1
1600 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1602 ; In this case, get multiple blocks from a specific TCP connection.
1604 ; On entry:
1605 ; ES:BX -> Buffer
1606 ; SI -> TFTP socket pointer
1607 ; CX -> 512-byte block count; 0FFFFh = until end of file
1608 ; On exit:
1609 ; SI -> TFTP socket pointer (or 0 on EOF)
1610 ; CF = 1 -> Hit EOF
1612 getfssec: push si
1613 push fs
1614 mov di,bx
1615 mov bx,si
1616 mov ax,pktbuf_seg
1617 mov fs,ax
1619 movzx ecx,cx
1620 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1621 jz .hit_eof ; Nothing to do?
1623 .need_more:
1624 push ecx
1626 movzx eax,word [bx+tftp_bytesleft]
1627 cmp ecx,eax
1628 jna .ok_size
1629 mov ecx,eax
1630 jcxz .need_packet ; No bytes available?
1631 .ok_size:
1633 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1634 mov si,[bx+tftp_dataptr]
1635 sub [bx+tftp_bytesleft],cx
1636 fs rep movsb ; Copy from packet buffer
1637 mov [bx+tftp_dataptr],si
1639 pop ecx
1640 sub ecx,eax
1641 jnz .need_more
1644 .hit_eof:
1645 pop fs
1646 pop si
1648 ; Is there anything left of this?
1649 mov eax,[si+tftp_filesize]
1650 sub eax,[si+tftp_filepos]
1651 jnz .bytes_left ; CF <- 0
1653 cmp [si+tftp_bytesleft],ax
1654 jnz .bytes_left ; CF <- 0
1656 ; The socket is closed and the buffer drained
1657 ; Close socket structure and re-init for next user
1658 call free_socket
1660 .bytes_left:
1664 ; No data in buffer, check to see if we can get a packet...
1666 .need_packet:
1667 pop ecx
1668 mov eax,[bx+tftp_filesize]
1669 cmp eax,[bx+tftp_filepos]
1670 je .hit_eof ; Already EOF'd; socket already closed
1672 pushad
1673 push es
1674 mov si,bx
1675 call get_packet
1676 pop es
1677 popad
1679 jmp .need_more
1682 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1684 get_packet:
1685 mov ax,ds
1686 mov es,ax
1688 .packet_loop:
1689 ; Start by ACKing the previous packet; this should cause the
1690 ; next packet to be sent.
1691 mov cx,PKT_RETRY
1692 mov word [PktTimeout],PKT_TIMEOUT
1694 .send_ack: push cx ; <D> Retry count
1696 mov ax,[si+tftp_lastpkt]
1697 call ack_packet ; Send ACK
1699 ; We used to test the error code here, but sometimes
1700 ; PXE would return negative status even though we really
1701 ; did send the ACK. Now, just treat a failed send as
1702 ; a normally lost packet, and let it time out in due
1703 ; course of events.
1705 .send_ok: ; Now wait for packet.
1706 mov dx,[BIOS_timer] ; Get current time
1708 mov cx,[PktTimeout]
1709 .wait_data: push cx ; <E> Timeout
1710 push dx ; <F> Old time
1712 mov bx,[si+tftp_pktbuf]
1713 mov [pxe_udp_read_pkt.buffer],bx
1714 mov [pxe_udp_read_pkt.buffer+2],fs
1715 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1716 mov eax,[si+tftp_remoteip]
1717 mov [pxe_udp_read_pkt.sip],eax
1718 mov eax,[MyIP]
1719 mov [pxe_udp_read_pkt.dip],eax
1720 mov ax,[si+tftp_remoteport]
1721 mov [pxe_udp_read_pkt.rport],ax
1722 mov ax,[si+tftp_localport]
1723 mov [pxe_udp_read_pkt.lport],ax
1724 mov di,pxe_udp_read_pkt
1725 mov bx,PXENV_UDP_READ
1726 push si ; <G>
1727 call pxenv
1728 pop si ; <G>
1729 and ax,ax
1730 jz .recv_ok
1732 ; No packet, or receive failure
1733 mov dx,[BIOS_timer]
1734 pop ax ; <F> Old time
1735 pop cx ; <E> Timeout
1736 cmp ax,dx ; Same time -> don't advance timeout
1737 je .wait_data ; Same clock tick
1738 loop .wait_data ; Decrease timeout
1740 pop cx ; <D> Didn't get any, send another ACK
1741 shl word [PktTimeout],1 ; Exponential backoff
1742 loop .send_ack
1743 jmp kaboom ; Forget it...
1745 .recv_ok: pop dx ; <F>
1746 pop cx ; <E>
1748 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1749 jb .wait_data ; Bad size for a DATA packet
1751 mov bx,[si+tftp_pktbuf]
1752 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1753 jne .wait_data ; Then wait for something else
1755 mov ax,[si+tftp_lastpkt]
1756 xchg ah,al ; Host byte order
1757 inc ax ; Which packet are we waiting for?
1758 xchg ah,al ; Network byte order
1759 cmp [fs:bx+2],ax
1760 je .right_packet
1762 ; Wrong packet, ACK the packet and then try again
1763 ; This is presumably because the ACK got lost,
1764 ; so the server just resent the previous packet
1765 mov ax,[fs:bx+2]
1766 call ack_packet
1767 jmp .send_ok ; Reset timeout
1769 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1771 pop cx ; <D> Don't need the retry count anymore
1773 mov [si+tftp_lastpkt],ax ; Update last packet number
1775 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1776 sub cx,byte 4 ; Skip TFTP header
1778 ; If this is a zero-length block, don't mess with the pointers,
1779 ; since we may have just set up the previous block that way
1780 jz .last_block
1782 ; Set pointer to data block
1783 lea ax,[bx+4] ; Data past TFTP header
1784 mov [si+tftp_dataptr],ax
1786 add [si+tftp_filepos],ecx
1787 mov [si+tftp_bytesleft],cx
1789 cmp cx,[si+tftp_blksize] ; Is it a full block?
1790 jb .last_block ; If so, it's not EOF
1792 ; If we had the exact right number of bytes, always get
1793 ; one more packet to get the (zero-byte) EOF packet and
1794 ; close the socket.
1795 mov eax,[si+tftp_filepos]
1796 cmp [si+tftp_filesize],eax
1797 je .packet_loop
1802 .last_block: ; Last block - ACK packet immediately
1803 mov ax,[fs:bx+2]
1804 call ack_packet
1806 ; Make sure we know we are at end of file
1807 mov eax,[si+tftp_filepos]
1808 mov [si+tftp_filesize],eax
1813 ; ack_packet:
1815 ; Send ACK packet. This is a common operation and so is worth canning.
1817 ; Entry:
1818 ; SI = TFTP block
1819 ; AX = Packet # to ack (network byte order)
1820 ; Exit:
1821 ; ZF = 0 -> Error
1822 ; All registers preserved
1824 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1826 ack_packet:
1827 pushad
1828 mov [ack_packet_buf+2],ax ; Packet number to ack
1829 mov ax,[si]
1830 mov [pxe_udp_write_pkt.lport],ax
1831 mov ax,[si+tftp_remoteport]
1832 mov [pxe_udp_write_pkt.rport],ax
1833 mov eax,[si+tftp_remoteip]
1834 mov [pxe_udp_write_pkt.sip],eax
1835 xor eax,[MyIP]
1836 and eax,[Netmask]
1837 jz .nogw
1838 mov eax,[Gateway]
1839 .nogw:
1840 mov [pxe_udp_write_pkt.gip],eax
1841 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1842 mov [pxe_udp_write_pkt.buffersize], word 4
1843 mov di,pxe_udp_write_pkt
1844 mov bx,PXENV_UDP_WRITE
1845 call pxenv
1846 cmp ax,byte 0 ; ZF = 1 if write OK
1847 popad
1851 ; unload_pxe:
1853 ; This function unloads the PXE and UNDI stacks and unclaims
1854 ; the memory.
1856 unload_pxe:
1857 test byte [KeepPXE],01h ; Should we keep PXE around?
1858 jnz reset_pxe
1860 push ds
1861 push es
1863 mov ax,cs
1864 mov ds,ax
1865 mov es,ax
1867 mov si,new_api_unload
1868 cmp byte [APIVer+1],2 ; Major API version >= 2?
1869 jae .new_api
1870 mov si,old_api_unload
1871 .new_api:
1873 .call_loop: xor ax,ax
1874 lodsb
1875 and ax,ax
1876 jz .call_done
1877 xchg bx,ax
1878 mov di,pxe_unload_stack_pkt
1879 push di
1880 xor ax,ax
1881 mov cx,pxe_unload_stack_pkt_len >> 1
1882 rep stosw
1883 pop di
1884 call pxenv
1885 jc .cant_free
1886 mov ax,word [pxe_unload_stack_pkt.status]
1887 cmp ax,PXENV_STATUS_SUCCESS
1888 jne .cant_free
1889 jmp .call_loop
1891 .call_done:
1892 mov bx,0FF00h
1894 mov dx,[RealBaseMem]
1895 cmp dx,[BIOS_fbm] ; Sanity check
1896 jna .cant_free
1897 inc bx
1899 ; Check that PXE actually unhooked the INT 1Ah chain
1900 movzx eax,word [4*0x1a]
1901 movzx ecx,word [4*0x1a+2]
1902 shl ecx,4
1903 add eax,ecx
1904 shr eax,10
1905 cmp ax,dx ; Not in range
1906 jae .ok
1907 cmp ax,[BIOS_fbm]
1908 jae .cant_free
1909 ; inc bx
1911 .ok:
1912 mov [BIOS_fbm],dx
1913 .pop_ret:
1914 pop es
1915 pop ds
1918 .cant_free:
1919 mov si,cant_free_msg
1920 call writestr
1921 push ax
1922 xchg bx,ax
1923 call writehex4
1924 mov al,'-'
1925 call writechr
1926 pop ax
1927 call writehex4
1928 mov al,'-'
1929 call writechr
1930 mov eax,[4*0x1a]
1931 call writehex8
1932 call crlf
1933 jmp .pop_ret
1935 ; We want to keep PXE around, but still we should reset
1936 ; it to the standard bootup configuration
1937 reset_pxe:
1938 push es
1939 push cs
1940 pop es
1941 mov bx,PXENV_UDP_CLOSE
1942 mov di,pxe_udp_close_pkt
1943 call pxenv
1944 pop es
1948 ; gendotquad
1950 ; Take an IP address (in network byte order) in EAX and
1951 ; output a dotted quad string to ES:DI.
1952 ; DI points to terminal null at end of string on exit.
1954 gendotquad:
1955 push eax
1956 push cx
1957 mov cx,4
1958 .genchar:
1959 push eax
1960 cmp al,10 ; < 10?
1961 jb .lt10 ; If so, skip first 2 digits
1963 cmp al,100 ; < 100
1964 jb .lt100 ; If so, skip first digit
1966 aam 100
1967 ; Now AH = 100-digit; AL = remainder
1968 add ah,'0'
1969 mov [es:di],ah
1970 inc di
1972 .lt100:
1973 aam 10
1974 ; Now AH = 10-digit; AL = remainder
1975 add ah,'0'
1976 mov [es:di],ah
1977 inc di
1979 .lt10:
1980 add al,'0'
1981 stosb
1982 mov al,'.'
1983 stosb
1984 pop eax
1985 ror eax,8 ; Move next char into LSB
1986 loop .genchar
1987 dec di
1988 mov [es:di], byte 0
1989 pop cx
1990 pop eax
1994 ; parse_dhcp
1996 ; Parse a DHCP packet. This includes dealing with "overloaded"
1997 ; option fields (see RFC 2132, section 9.3)
1999 ; This should fill in the following global variables, if the
2000 ; information is present:
2002 ; MyIP - client IP address
2003 ; ServerIP - boot server IP address
2004 ; Netmask - network mask
2005 ; Gateway - default gateway router IP
2006 ; BootFile - boot file name
2007 ; DNSServers - DNS server IPs
2008 ; LocalDomain - Local domain name
2010 ; This assumes the DHCP packet is in "trackbuf" and the length
2011 ; of the packet in in CX on entry.
2014 parse_dhcp:
2015 mov byte [OverLoad],0 ; Assume no overload
2016 mov eax, [trackbuf+bootp.yip]
2017 and eax, eax
2018 jz .noyip
2019 cmp al,224 ; Class D or higher -> bad
2020 jae .noyip
2021 mov [MyIP], eax
2022 .noyip:
2023 mov eax, [trackbuf+bootp.sip]
2024 and eax, eax
2025 jz .nosip
2026 cmp al,224 ; Class D or higher -> bad
2027 jae .nosip
2028 mov [ServerIP], eax
2029 .nosip:
2030 sub cx, bootp.options
2031 jbe .nooptions
2032 mov si, trackbuf+bootp.option_magic
2033 lodsd
2034 cmp eax, BOOTP_OPTION_MAGIC
2035 jne .nooptions
2036 call parse_dhcp_options
2037 .nooptions:
2038 mov si, trackbuf+bootp.bootfile
2039 test byte [OverLoad],1
2040 jz .nofileoverload
2041 mov cx,128
2042 call parse_dhcp_options
2043 jmp short .parsed_file
2044 .nofileoverload:
2045 cmp byte [si], 0
2046 jz .parsed_file ; No bootfile name
2047 mov di,BootFile
2048 mov cx,32
2049 rep movsd
2050 xor al,al
2051 stosb ; Null-terminate
2052 .parsed_file:
2053 mov si, trackbuf+bootp.sname
2054 test byte [OverLoad],2
2055 jz .nosnameoverload
2056 mov cx,64
2057 call parse_dhcp_options
2058 .nosnameoverload:
2062 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2063 ; size is CX -- some DHCP servers leave option fields unterminated
2064 ; in violation of the spec.
2066 ; For parse_some_dhcp_options, DH contains the minimum value for
2067 ; the option to recognize -- this is used to restrict parsing to
2068 ; PXELINUX-specific options only.
2070 parse_dhcp_options:
2071 xor dx,dx
2073 parse_some_dhcp_options:
2074 .loop:
2075 and cx,cx
2076 jz .done
2078 lodsb
2079 dec cx
2080 jz .done ; Last byte; must be PAD, END or malformed
2081 cmp al, 0 ; PAD option
2082 je .loop
2083 cmp al,255 ; END option
2084 je .done
2086 ; Anything else will have a length field
2087 mov dl,al ; DL <- option number
2088 xor ax,ax
2089 lodsb ; AX <- option length
2090 dec cx
2091 sub cx,ax ; Decrement bytes left counter
2092 jb .done ; Malformed option: length > field size
2094 cmp dl,dh ; Is the option value valid?
2095 jb .opt_done
2097 cmp dl,1 ; SUBNET MASK option
2098 jne .not_subnet
2099 mov ebx,[si]
2100 mov [Netmask],ebx
2101 jmp .opt_done
2102 .not_subnet:
2104 cmp dl,3 ; ROUTER option
2105 jne .not_router
2106 mov ebx,[si]
2107 mov [Gateway],ebx
2108 jmp .opt_done
2109 .not_router:
2111 cmp dl,6 ; DNS SERVERS option
2112 jne .not_dns
2113 pusha
2114 mov cx,ax
2115 shr cx,2
2116 cmp cl,DNS_MAX_SERVERS
2117 jna .oklen
2118 mov cl,DNS_MAX_SERVERS
2119 .oklen:
2120 mov di,DNSServers
2121 rep movsd
2122 mov [LastDNSServer],di
2123 popa
2124 jmp .opt_done
2125 .not_dns:
2127 cmp dl,15 ; DNS LOCAL DOMAIN option
2128 jne .not_localdomain
2129 pusha
2130 mov bx,si
2131 add bx,ax
2132 xor ax,ax
2133 xchg [bx],al ; Zero-terminate option
2134 mov di,LocalDomain
2135 call dns_mangle ; Convert to DNS label set
2136 mov [bx],al ; Restore ending byte
2137 popa
2138 jmp .opt_done
2139 .not_localdomain:
2141 cmp dl,43 ; VENDOR ENCAPSULATED option
2142 jne .not_vendor
2143 pusha
2144 mov dh,208 ; Only recognize PXELINUX options
2145 mov cx,ax ; Length of option = max bytes to parse
2146 call parse_some_dhcp_options ; Parse recursive structure
2147 popa
2148 jmp .opt_done
2149 .not_vendor:
2151 cmp dl,52 ; OPTION OVERLOAD option
2152 jne .not_overload
2153 mov bl,[si]
2154 mov [OverLoad],bl
2155 jmp .opt_done
2156 .not_overload:
2158 cmp dl,67 ; BOOTFILE NAME option
2159 jne .not_bootfile
2160 mov di,BootFile
2161 jmp short .copyoption
2162 .done:
2163 ret ; This is here to make short jumps easier
2164 .not_bootfile:
2166 cmp dl,208 ; PXELINUX MAGIC option
2167 jne .not_pl_magic
2168 cmp al,4 ; Must have length == 4
2169 jne .opt_done
2170 cmp dword [si], htonl(0xF100747E) ; Magic number
2171 jne .opt_done
2172 or byte [DHCPMagic], byte 1 ; Found magic #
2173 jmp short .opt_done
2174 .not_pl_magic:
2176 cmp dl,209 ; PXELINUX CONFIGFILE option
2177 jne .not_pl_config
2178 mov di,ConfigName
2179 or byte [DHCPMagic], byte 2 ; Got config file
2180 jmp short .copyoption
2181 .not_pl_config:
2183 cmp dl,210 ; PXELINUX PATHPREFIX option
2184 jne .not_pl_prefix
2185 mov di,PathPrefix
2186 or byte [DHCPMagic], byte 4 ; Got path prefix
2187 jmp short .copyoption
2188 .not_pl_prefix:
2190 cmp dl,211 ; PXELINUX REBOOTTIME option
2191 jne .not_pl_timeout
2192 cmp al,4
2193 jne .opt_done
2194 mov ebx,[si]
2195 xchg bl,bh ; Convert to host byte order
2196 rol ebx,16
2197 xchg bl,bh
2198 mov [RebootTime],ebx
2199 or byte [DHCPMagic], byte 8 ; Got RebootTime
2200 ; jmp short .opt_done
2201 .not_pl_timeout:
2203 ; Unknown option. Skip to the next one.
2204 .opt_done:
2205 add si,ax
2206 .opt_done_noskip:
2207 jmp .loop
2209 ; Common code for copying an option verbatim
2210 .copyoption:
2211 xchg cx,ax
2212 rep movsb
2213 xchg cx,ax ; Now ax == 0
2214 stosb ; Null-terminate
2215 jmp short .opt_done_noskip
2218 ; genipopt
2220 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2221 ; option into IPOption based on a DHCP packet in trackbuf.
2222 ; Assumes CS == DS == ES.
2224 genipopt:
2225 pushad
2226 mov di,IPOption
2227 mov eax,'ip='
2228 stosd
2229 dec di
2230 mov eax,[MyIP]
2231 call gendotquad
2232 mov al,':'
2233 stosb
2234 mov eax,[ServerIP]
2235 call gendotquad
2236 mov al,':'
2237 stosb
2238 mov eax,[Gateway]
2239 call gendotquad
2240 mov al,':'
2241 stosb
2242 mov eax,[Netmask]
2243 call gendotquad ; Zero-terminates its output
2244 sub di,IPOption
2245 mov [IPOptionLen],di
2246 popad
2250 ; Call the receive loop while idle. This is done mostly so we can respond to
2251 ; ARP messages, but perhaps in the future this can be used to do network
2252 ; console.
2254 ; hpa sez: people using automatic control on the serial port get very
2255 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2256 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2257 ; passed since the last poll, and reset this when a character is
2258 ; received (RESET_IDLE).
2260 reset_idle:
2261 push ax
2262 mov ax,[cs:BIOS_timer]
2263 mov [cs:IdleTimer],ax
2264 pop ax
2267 check_for_arp:
2268 push ax
2269 mov ax,[cs:BIOS_timer]
2270 sub ax,[cs:IdleTimer]
2271 cmp ax,4
2272 pop ax
2273 jae .need_poll
2275 .need_poll: pushad
2276 push ds
2277 push es
2278 mov ax,cs
2279 mov ds,ax
2280 mov es,ax
2281 mov di,packet_buf
2282 mov [pxe_udp_read_pkt.status],al ; 0
2283 mov [pxe_udp_read_pkt.buffer],di
2284 mov [pxe_udp_read_pkt.buffer+2],ds
2285 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2286 mov eax,[MyIP]
2287 mov [pxe_udp_read_pkt.dip],eax
2288 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2289 mov di,pxe_udp_read_pkt
2290 mov bx,PXENV_UDP_READ
2291 call pxenv
2292 ; Ignore result...
2293 pop es
2294 pop ds
2295 popad
2296 RESET_IDLE
2299 ; -----------------------------------------------------------------------------
2300 ; Common modules
2301 ; -----------------------------------------------------------------------------
2303 %include "getc.inc" ; getc et al
2304 %include "conio.inc" ; Console I/O
2305 %include "writestr.inc" ; String output
2306 writestr equ cwritestr
2307 %include "writehex.inc" ; Hexadecimal output
2308 %include "parseconfig.inc" ; High-level config file handling
2309 %include "parsecmd.inc" ; Low-level config file handling
2310 %include "bcopy32.inc" ; 32-bit bcopy
2311 %include "loadhigh.inc" ; Load a file into high memory
2312 %include "font.inc" ; VGA font stuff
2313 %include "graphics.inc" ; VGA graphics
2314 %include "highmem.inc" ; High memory sizing
2315 %include "strcpy.inc" ; strcpy()
2316 %include "rawcon.inc" ; Console I/O w/o using the console functions
2317 %include "dnsresolv.inc" ; DNS resolver
2319 ; -----------------------------------------------------------------------------
2320 ; Begin data section
2321 ; -----------------------------------------------------------------------------
2323 section .data
2325 hextbl_lower db '0123456789abcdef'
2326 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2327 db CR, LF, 0
2328 boot_prompt db 'boot: ', 0
2329 wipe_char db BS, ' ', BS, 0
2330 err_notfound db 'Could not find kernel image: ',0
2331 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
2332 err_noram db 'It appears your computer has less than '
2333 asciidec dosram_k
2334 db 'K of low ("DOS")'
2335 db CR, LF
2336 db 'RAM. Linux needs at least this amount to boot. If you get'
2337 db CR, LF
2338 db 'this message in error, hold down the Ctrl key while'
2339 db CR, LF
2340 db 'booting, and I will take your word for it.', CR, LF, 0
2341 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
2342 err_noparm db 'Missing parameter in config file.', CR, LF, 0
2343 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
2344 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
2345 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
2346 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
2347 db CR, LF, 0
2348 err_notdos db ': attempted DOS system call', CR, LF, 0
2349 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
2350 err_bssimage db 'BSS images not supported.', CR, LF, 0
2351 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
2352 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2353 bailmsg equ err_bootfailed
2354 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2355 err_pxefailed db 'PXE API call failed, error ', 0
2356 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2357 err_noconfig db 'Unable to locate configuration file', CR, LF, 0
2358 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2359 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2360 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2361 apiver_str db 'PXE API version is ',0
2362 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2363 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2364 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2365 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2366 undi_data_msg db 'UNDI data segment at: ',0
2367 undi_data_len_msg db 'UNDI data segment size: ',0
2368 undi_code_msg db 'UNDI code segment at: ',0
2369 undi_code_len_msg db 'UNDI code segment size: ',0
2370 cant_free_msg db 'Failed to free base memory, error ', 0
2371 notfound_msg db 'not found', CR, LF, 0
2372 myipaddr_msg db 'My IP address seems to be ',0
2373 tftpprefix_msg db 'TFTP prefix: ', 0
2374 localboot_msg db 'Booting from local disk...', CR, LF, 0
2375 cmdline_msg db 'Command line: ', CR, LF, 0
2376 ready_msg db 'Ready.', CR, LF, 0
2377 trying_msg db 'Trying to load: ', 0
2378 crlfloading_msg db CR, LF ; Fall through
2379 loading_msg db 'Loading ', 0
2380 dotdot_msg db '.'
2381 dot_msg db '.', 0
2382 fourbs_msg db BS, BS, BS, BS, 0
2383 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
2384 crlf_msg db CR, LF
2385 null_msg db 0
2386 crff_msg db CR, FF, 0
2387 default_str db 'default', 0
2388 default_len equ ($-default_str)
2389 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2390 cfgprefix db 'pxelinux.cfg/' ; No final null!
2391 cfgprefix_len equ ($-cfgprefix)
2394 ; Command line options we'd like to take a look at
2396 ; mem= and vga= are handled as normal 32-bit integer values
2397 initrd_cmd db 'initrd='
2398 initrd_cmd_len equ $-initrd_cmd
2400 ; This one we make ourselves
2401 bootif_str db 'BOOTIF='
2402 bootif_str_len equ $-bootif_str
2404 ; Config file keyword table
2406 %include "keywords.inc"
2409 ; Extensions to search for (in *forward* order).
2410 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2412 align 4, db 0
2413 exten_table: db '.cbt' ; COMBOOT (specific)
2414 db '.0', 0, 0 ; PXE bootstrap program
2415 db '.com' ; COMBOOT (same as DOS)
2416 db '.c32' ; COM32
2417 exten_table_end:
2418 dd 0, 0 ; Need 8 null bytes here
2421 ; PXE unload sequences
2423 new_api_unload:
2424 db PXENV_UDP_CLOSE
2425 db PXENV_UNDI_SHUTDOWN
2426 db PXENV_UNLOAD_STACK
2427 db PXENV_STOP_UNDI
2428 db 0
2429 old_api_unload:
2430 db PXENV_UDP_CLOSE
2431 db PXENV_UNDI_SHUTDOWN
2432 db PXENV_UNLOAD_STACK
2433 db PXENV_UNDI_CLEANUP
2434 db 0
2437 ; PXE query packets partially filled in
2439 pxe_bootp_query_pkt_2:
2440 .status: dw 0 ; Status
2441 .packettype: dw 2 ; DHCPACK packet
2442 .buffersize: dw trackbufsize ; Packet size
2443 .buffer: dw trackbuf, 0 ; seg:off of buffer
2444 .bufferlimit: dw trackbufsize ; Unused
2446 pxe_bootp_query_pkt_3:
2447 .status: dw 0 ; Status
2448 .packettype: dw 3 ; Boot server packet
2449 .buffersize: dw trackbufsize ; Packet size
2450 .buffer: dw trackbuf, 0 ; seg:off of buffer
2451 .bufferlimit: dw trackbufsize ; Unused
2453 pxe_bootp_size_query_pkt:
2454 .status: dw 0 ; Status
2455 .packettype: dw 2 ; DHCPACK packet
2456 .buffersize: dw 0 ; Packet size
2457 .buffer: dw 0, 0 ; seg:off of buffer
2458 .bufferlimit: dw 0 ; Unused
2460 pxe_udp_open_pkt:
2461 .status: dw 0 ; Status
2462 .sip: dd 0 ; Source (our) IP
2464 pxe_udp_close_pkt:
2465 .status: dw 0 ; Status
2467 pxe_udp_write_pkt:
2468 .status: dw 0 ; Status
2469 .sip: dd 0 ; Server IP
2470 .gip: dd 0 ; Gateway IP
2471 .lport: dw 0 ; Local port
2472 .rport: dw 0 ; Remote port
2473 .buffersize: dw 0 ; Size of packet
2474 .buffer: dw 0, 0 ; seg:off of buffer
2476 pxe_udp_read_pkt:
2477 .status: dw 0 ; Status
2478 .sip: dd 0 ; Source IP
2479 .dip: dd 0 ; Destination (our) IP
2480 .rport: dw 0 ; Remote port
2481 .lport: dw 0 ; Local port
2482 .buffersize: dw 0 ; Max packet size
2483 .buffer: dw 0, 0 ; seg:off of buffer
2486 ; Misc initialized (data) variables
2488 alignb 4, db 0
2489 BaseStack dd StackBuf ; ESP of base stack
2490 dw 0 ; SS of base stack
2491 NextSocket dw 49152 ; Counter for allocating socket numbers
2492 KeepPXE db 0 ; Should PXE be kept around?
2495 ; TFTP commands
2497 tftp_tail db 'octet', 0 ; Octet mode
2498 tsize_str db 'tsize' ,0 ; Request size
2499 tsize_len equ ($-tsize_str)
2500 db '0', 0
2501 blksize_str db 'blksize', 0 ; Request large blocks
2502 blksize_len equ ($-blksize_str)
2503 asciidec TFTP_LARGEBLK
2504 db 0
2505 tftp_tail_len equ ($-tftp_tail)
2507 alignb 2, db 0
2509 ; Options negotiation parsing table (string pointer, string len, offset
2510 ; into socket structure)
2512 tftp_opt_table:
2513 dw tsize_str, tsize_len, tftp_filesize
2514 dw blksize_str, blksize_len, tftp_blksize
2515 tftp_opts equ ($-tftp_opt_table)/6
2518 ; Error packet to return on options negotiation error
2520 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2521 dw TFTP_EOPTNEG ; ERROR 8: bad options
2522 db 'tsize option required', 0 ; Error message
2523 tftp_opt_err_len equ ($-tftp_opt_err)
2525 alignb 4, db 0
2526 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2529 ; IP information (initialized to "unknown" values)
2530 MyIP dd 0 ; My IP address
2531 ServerIP dd 0 ; IP address of boot server
2532 Netmask dd 0 ; Netmask of this subnet
2533 Gateway dd 0 ; Default router
2534 ServerPort dw TFTP_PORT ; TFTP server port
2537 ; Variables that are uninitialized in SYSLINUX but initialized here
2539 alignb 4, db 0
2540 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2541 BufSafeSec dw trackbufsize/512 ; = how many sectors?
2542 BufSafeBytes dw trackbufsize ; = how many bytes?
2543 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
2544 %ifndef DEPEND
2545 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2546 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2547 %endif
2548 %endif
2549 IPAppend db 0 ; Default IPAPPEND option
2550 DHCPMagic db 0 ; DHCP site-specific option info