Target to upload a prerelease
[syslinux.git] / pxelinux.asm
blob5809a471029e33299b624ee8acf2979694146a70
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 jmp kaboom
843 .success:
846 ; Now we have the config file open. Parse the config file and
847 ; run the user interface.
849 %include "ui.inc"
852 ; Linux kernel loading code is common. However, we need to define
853 ; a couple of helper macros...
856 ; Handle "ipappend" option
857 %define HAVE_SPECIAL_APPEND
858 %macro SPECIAL_APPEND 0
859 test byte [IPAppend],01h ; ip=
860 jz .noipappend1
861 mov si,IPOption
862 mov cx,[IPOptionLen]
863 rep movsb
864 mov al,' '
865 stosb
866 .noipappend1:
867 test byte [IPAppend],02h
868 jz .noipappend2
869 mov si,BOOTIFStr
870 call strcpy
871 mov byte [es:di-1],' ' ; Replace null with space
872 .noipappend2:
873 %endmacro
875 ; Unload PXE stack
876 %define HAVE_UNLOAD_PREP
877 %macro UNLOAD_PREP 0
878 call unload_pxe
879 %endmacro
881 %include "runkernel.inc"
884 ; COMBOOT-loading code
886 %include "comboot.inc"
887 %include "com32.inc"
888 %include "cmdline.inc"
891 ; Boot sector loading code
893 %include "bootsect.inc"
896 ; Boot to the local disk by returning the appropriate PXE magic.
897 ; AX contains the appropriate return code.
899 local_boot:
900 push cs
901 pop ds
902 mov [LocalBootType],ax
903 call vgaclearmode
904 mov si,localboot_msg
905 call writestr
906 ; Restore the environment we were called with
907 lss sp,[InitStack]
908 pop gs
909 pop fs
910 pop es
911 pop ds
912 popad
913 mov ax,[cs:LocalBootType]
914 popfd
915 retf ; Return to PXE
918 ; Abort loading code
920 %include "abort.inc"
923 ; kaboom: write a message and bail out. Wait for quite a while,
924 ; or a user keypress, then do a hard reboot.
926 kaboom:
927 mov ax,cs
928 mov es,ax
929 mov ds,ax
930 lss esp,[BaseStack]
932 .patch: mov si,bailmsg
933 call writestr ; Returns with AL = 0
934 .drain: call pollchar
935 jz .drained
936 call getchar
937 jmp short .drain
938 .drained:
939 mov edi,[RebootTime]
940 mov al,[DHCPMagic]
941 and al,09h ; Magic+Timeout
942 cmp al,09h
943 je .time_set
944 mov edi,REBOOT_TIME
945 .time_set:
946 mov cx,18
947 .wait1: push cx
948 mov ecx,edi
949 .wait2: mov dx,[BIOS_timer]
950 .wait3: call pollchar
951 jnz .keypress
952 cmp dx,[BIOS_timer]
953 je .wait3
954 loop .wait2,ecx
955 mov al,'.'
956 call writechr
957 pop cx
958 loop .wait1
959 .keypress:
960 call crlf
961 mov word [BIOS_magic],0 ; Cold reboot
962 jmp 0F000h:0FFF0h ; Reset vector address
965 ; memory_scan_for_pxe_struct:
967 ; If none of the standard methods find the !PXE structure, look for it
968 ; by scanning memory.
970 ; On exit, if found:
971 ; CF = 0, ES:BX -> !PXE structure
972 ; Otherwise CF = 1, all registers saved
974 memory_scan_for_pxe_struct:
975 push ds
976 pusha
977 mov ax,cs
978 mov ds,ax
979 mov si,trymempxe_msg
980 call writestr
981 mov ax,[BIOS_fbm] ; Starting segment
982 shl ax,(10-4) ; Kilobytes -> paragraphs
983 ; mov ax,01000h ; Start to look here
984 dec ax ; To skip inc ax
985 .mismatch:
986 inc ax
987 cmp ax,0A000h ; End of memory
988 jae .not_found
989 call writehex4
990 mov si,fourbs_msg
991 call writestr
992 mov es,ax
993 mov edx,[es:0]
994 cmp edx,'!PXE'
995 jne .mismatch
996 movzx cx,byte [es:4] ; Length of structure
997 cmp cl,08h ; Minimum length
998 jb .mismatch
999 push ax
1000 xor ax,ax
1001 xor si,si
1002 .checksum: es lodsb
1003 add ah,al
1004 loop .checksum
1005 pop ax
1006 jnz .mismatch ; Checksum must == 0
1007 .found: mov bp,sp
1008 xor bx,bx
1009 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
1010 mov ax,es
1011 call writehex4
1012 call crlf
1013 popa
1014 pop ds
1017 .not_found: mov si,notfound_msg
1018 call writestr
1019 popa
1020 pop ds
1025 ; memory_scan_for_pxenv_struct:
1027 ; If none of the standard methods find the PXENV+ structure, look for it
1028 ; by scanning memory.
1030 ; On exit, if found:
1031 ; CF = 0, ES:BX -> PXENV+ structure
1032 ; Otherwise CF = 1, all registers saved
1034 memory_scan_for_pxenv_struct:
1035 pusha
1036 mov si,trymempxenv_msg
1037 call writestr
1038 ; mov ax,[BIOS_fbm] ; Starting segment
1039 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1040 mov ax,01000h ; Start to look here
1041 dec ax ; To skip inc ax
1042 .mismatch:
1043 inc ax
1044 cmp ax,0A000h ; End of memory
1045 jae .not_found
1046 mov es,ax
1047 mov edx,[es:0]
1048 cmp edx,'PXEN'
1049 jne .mismatch
1050 mov dx,[es:4]
1051 cmp dx,'V+'
1052 jne .mismatch
1053 movzx cx,byte [es:8] ; Length of structure
1054 cmp cl,26h ; Minimum length
1055 jb .mismatch
1056 xor ax,ax
1057 xor si,si
1058 .checksum: es lodsb
1059 add ah,al
1060 loop .checksum
1061 and ah,ah
1062 jnz .mismatch ; Checksum must == 0
1063 .found: mov bp,sp
1064 mov [bp+8],bx ; Save BX into stack frame
1065 mov ax,bx
1066 call writehex4
1067 call crlf
1070 .not_found: mov si,notfound_msg
1071 call writestr
1072 popad
1077 ; searchdir:
1079 ; Open a TFTP connection to the server
1081 ; On entry:
1082 ; DS:DI = mangled filename
1083 ; If successful:
1084 ; ZF clear
1085 ; SI = socket pointer
1086 ; DX:AX = file length in bytes
1087 ; If unsuccessful
1088 ; ZF set
1091 searchdir:
1092 push es
1093 push bx
1094 push cx
1095 mov ax,ds
1096 mov es,ax
1097 mov si,di
1098 push bp
1099 mov bp,sp
1101 call allocate_socket
1102 jz .ret
1104 mov ax,PKT_RETRY ; Retry counter
1105 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1107 .sendreq: push ax ; [bp-2] - Retry counter
1108 push si ; [bp-4] - File name
1110 mov di,packet_buf
1111 mov [pxe_udp_write_pkt.buffer],di
1113 mov ax,TFTP_RRQ ; TFTP opcode
1114 stosw
1116 lodsd ; EAX <- server override (if any)
1117 and eax,eax
1118 jnz .noprefix ; No prefix, and we have the server
1120 push si ; Add common prefix
1121 mov si,PathPrefix
1122 call strcpy
1123 dec di
1124 pop si
1126 mov eax,[ServerIP] ; Get default server
1128 .noprefix:
1129 call strcpy ; Filename
1131 mov [bx+tftp_remoteip],eax
1133 push bx ; [bp-6] - TFTP block
1134 mov bx,[bx]
1135 push bx ; [bp-8] - TID (local port no)
1137 mov [pxe_udp_write_pkt.status],byte 0
1138 mov [pxe_udp_write_pkt.sip],eax
1139 ; Now figure out the gateway
1140 xor eax,[MyIP]
1141 and eax,[Netmask]
1142 jz .nogwneeded
1143 mov eax,[Gateway]
1144 .nogwneeded:
1145 mov [pxe_udp_write_pkt.gip],eax
1146 mov [pxe_udp_write_pkt.lport],bx
1147 mov ax,[ServerPort]
1148 mov [pxe_udp_write_pkt.rport],ax
1149 mov si,tftp_tail
1150 mov cx,tftp_tail_len
1151 rep movsb
1152 sub di,packet_buf ; Get packet size
1153 mov [pxe_udp_write_pkt.buffersize],di
1155 mov di,pxe_udp_write_pkt
1156 mov bx,PXENV_UDP_WRITE
1157 call pxenv
1158 jc .failure
1159 cmp word [pxe_udp_write_pkt.status],byte 0
1160 jne .failure
1163 ; Danger, Will Robinson! We need to support timeout
1164 ; and retry lest we just lost a packet...
1167 ; Packet transmitted OK, now we need to receive
1168 .getpacket: push word [PktTimeout] ; [bp-10]
1169 push word [BIOS_timer] ; [bp-12]
1171 .pkt_loop: mov bx,[bp-8] ; TID
1172 mov di,packet_buf
1173 mov word [pxe_udp_read_pkt.status],0
1174 mov [pxe_udp_read_pkt.buffer],di
1175 mov [pxe_udp_read_pkt.buffer+2],ds
1176 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1177 mov eax,[MyIP]
1178 mov [pxe_udp_read_pkt.dip],eax
1179 mov [pxe_udp_read_pkt.lport],bx
1180 mov di,pxe_udp_read_pkt
1181 mov bx,PXENV_UDP_READ
1182 call pxenv
1183 and ax,ax
1184 jz .got_packet ; Wait for packet
1185 .no_packet:
1186 mov dx,[BIOS_timer]
1187 cmp dx,[bp-12]
1188 je .pkt_loop
1189 mov [bp-12],dx
1190 dec word [bp-10] ; Timeout
1191 jnz .pkt_loop
1192 pop ax ; Adjust stack
1193 pop ax
1194 shl word [PktTimeout],1 ; Exponential backoff
1195 jmp .failure
1197 .got_packet:
1198 mov si,[bp-6] ; TFTP pointer
1199 mov bx,[bp-8] ; TID
1201 mov eax,[si+tftp_remoteip]
1202 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1203 jne .no_packet
1205 ; Got packet - reset timeout
1206 mov word [PktTimeout],PKT_TIMEOUT
1208 pop ax ; Adjust stack
1209 pop ax
1211 mov ax,[pxe_udp_read_pkt.rport]
1212 mov [si+tftp_remoteport],ax
1214 ; filesize <- -1 == unknown
1215 mov dword [si+tftp_filesize], -1
1216 ; Default blksize unless blksize option negotiated
1217 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1219 mov cx,[pxe_udp_read_pkt.buffersize]
1220 sub cx,2 ; CX <- bytes after opcode
1221 jb .failure ; Garbled reply
1223 mov si,packet_buf
1224 lodsw
1226 cmp ax, TFTP_ERROR
1227 je .bailnow ; ERROR reply: don't try again
1229 cmp ax, TFTP_OACK
1230 jne .no_tsize
1232 ; Now we need to parse the OACK packet to get the transfer
1233 ; size. SI -> first byte of options; CX -> byte count
1234 .parse_oack:
1235 jcxz .no_tsize ; No options acked
1236 .get_opt_name:
1237 mov di,si
1238 mov bx,si
1239 .opt_name_loop: lodsb
1240 and al,al
1241 jz .got_opt_name
1242 or al,20h ; Convert to lowercase
1243 stosb
1244 loop .opt_name_loop
1245 ; We ran out, and no final null
1246 jmp .err_reply
1247 .got_opt_name: ; si -> option value
1248 dec cx ; bytes left in pkt
1249 jz .err_reply ; Option w/o value
1251 ; Parse option pointed to by bx; guaranteed to be
1252 ; null-terminated.
1253 push cx
1254 push si
1255 mov si,bx ; -> option name
1256 mov bx,tftp_opt_table
1257 mov cx,tftp_opts
1258 .opt_loop:
1259 push cx
1260 push si
1261 mov di,[bx] ; Option pointer
1262 mov cx,[bx+2] ; Option len
1263 repe cmpsb
1264 pop si
1265 pop cx
1266 je .get_value ; OK, known option
1267 add bx,6
1268 loop .opt_loop
1270 pop si
1271 pop cx
1272 jmp .err_reply ; Non-negotiated option returned
1274 .get_value: pop si ; si -> option value
1275 pop cx ; cx -> bytes left in pkt
1276 mov bx,[bx+4] ; Pointer to data target
1277 add bx,[bp-6] ; TFTP socket pointer
1278 xor eax,eax
1279 xor edx,edx
1280 .value_loop: lodsb
1281 and al,al
1282 jz .got_value
1283 sub al,'0'
1284 cmp al, 9
1285 ja .err_reply ; Not a decimal digit
1286 imul edx,10
1287 add edx,eax
1288 mov [bx],edx
1289 loop .value_loop
1290 ; Ran out before final null, accept anyway
1291 jmp short .done_pkt
1293 .got_value:
1294 dec cx
1295 jnz .get_opt_name ; Not end of packet
1297 ; ZF == 1
1299 ; Success, done!
1300 .done_pkt:
1301 pop si ; Junk
1302 pop si ; We want the packet ptr in SI
1304 mov eax,[si+tftp_filesize]
1305 cmp eax,-1
1306 jz .no_tsize
1307 mov edx,eax
1308 shr edx,16 ; DX:AX == EAX
1310 and eax,eax ; Set ZF depending on file size
1311 pop bp ; Junk
1312 pop bp ; Junk (retry counter)
1313 jz .error_si ; ZF = 1 need to free the socket
1314 .ret:
1315 pop bp
1316 pop cx
1317 pop bx
1318 pop es
1321 .no_tsize:
1322 .err_reply: ; Option negotiation error. Send ERROR reply.
1323 ; ServerIP and gateway are already programmed in
1324 mov si,[bp-6]
1325 mov ax,[si+tftp_remoteport]
1326 mov word [pxe_udp_write_pkt.rport],ax
1327 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1328 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1329 mov di,pxe_udp_write_pkt
1330 mov bx,PXENV_UDP_WRITE
1331 call pxenv
1333 ; Write an error message and explode
1334 mov si,err_oldtftp
1335 call writestr
1336 jmp kaboom
1338 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1340 .failure: pop bx ; Junk
1341 pop bx
1342 pop si
1343 pop ax
1344 dec ax ; Retry counter
1345 jnz .sendreq ; Try again
1347 .error: mov si,bx ; Socket pointer
1348 .error_si: ; Socket pointer already in SI
1349 call free_socket ; ZF <- 1, SI <- 0
1350 jmp .ret
1353 ; allocate_socket: Allocate a local UDP port structure
1355 ; If successful:
1356 ; ZF set
1357 ; BX = socket pointer
1358 ; If unsuccessful:
1359 ; ZF clear
1361 allocate_socket:
1362 push cx
1363 mov bx,Files
1364 mov cx,MAX_OPEN
1365 .check: cmp word [bx], byte 0
1366 je .found
1367 add bx,open_file_t_size
1368 loop .check
1369 xor cx,cx ; ZF = 1
1370 pop cx
1372 ; Allocate a socket number. Socket numbers are made
1373 ; guaranteed unique by including the socket slot number
1374 ; (inverted, because we use the loop counter cx); add a
1375 ; counter value to keep the numbers from being likely to
1376 ; get immediately reused.
1378 ; The NextSocket variable also contains the top two bits
1379 ; set. This generates a value in the range 49152 to
1380 ; 57343.
1381 .found:
1382 dec cx
1383 push ax
1384 mov ax,[NextSocket]
1385 inc ax
1386 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1387 mov [NextSocket],ax
1388 shl cx,13-MAX_OPEN_LG2
1389 add cx,ax ; ZF = 0
1390 xchg ch,cl ; Convert to network byte order
1391 mov [bx],cx ; Socket in use
1392 pop ax
1393 pop cx
1397 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1399 free_socket:
1400 push es
1401 pusha
1402 xor ax,ax
1403 mov es,ax
1404 mov di,si
1405 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1406 rep stosw
1407 popa
1408 pop es
1409 xor si,si
1413 ; parse_dotquad:
1414 ; Read a dot-quad pathname in DS:SI and output an IP
1415 ; address in EAX, with SI pointing to the first
1416 ; nonmatching character.
1418 ; Return CF=1 on error.
1420 parse_dotquad:
1421 push cx
1422 mov cx,4
1423 xor eax,eax
1424 .parseloop:
1425 mov ch,ah
1426 mov ah,al
1427 lodsb
1428 sub al,'0'
1429 jb .notnumeric
1430 cmp al,9
1431 ja .notnumeric
1432 aad ; AL += 10 * AH; AH = 0;
1433 xchg ah,ch
1434 jmp .parseloop
1435 .notnumeric:
1436 cmp al,'.'-'0'
1437 pushf
1438 mov al,ah
1439 mov ah,ch
1440 xor ch,ch
1441 ror eax,8
1442 popf
1443 jne .error
1444 loop .parseloop
1445 jmp .done
1446 .error:
1447 loop .realerror ; If CX := 1 then we're done
1449 jmp .done
1450 .realerror:
1452 .done:
1453 dec si ; CF unchanged!
1454 pop cx
1457 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1458 ; to by ES:DI; ends on encountering any whitespace.
1460 ; This verifies that a filename is < FILENAME_MAX characters
1461 ; and doesn't contain whitespace, and zero-pads the output buffer,
1462 ; so "repe cmpsb" can do a compare.
1464 ; The first four bytes of the manged name is the IP address of
1465 ; the download host.
1467 mangle_name:
1468 push si
1469 mov eax,[ServerIP]
1470 cmp byte [si],0
1471 je .noip ; Null filename?!?!
1472 cmp word [si],'::' ; Leading ::?
1473 je .gotprefix
1475 .more:
1476 inc si
1477 cmp byte [si],0
1478 je .noip
1479 cmp word [si],'::'
1480 jne .more
1482 ; We have a :: prefix of some sort, it could be either
1483 ; a DNS name or a dot-quad IP address. Try the dot-quad
1484 ; first...
1485 .here:
1486 pop si
1487 push si
1488 call parse_dotquad
1489 jc .notdq
1490 cmp word [si],'::'
1491 je .gotprefix
1492 .notdq:
1493 pop si
1494 push si
1495 call dns_resolv
1496 cmp word [si],'::'
1497 jne .noip
1498 and eax,eax
1499 jnz .gotprefix
1501 .noip:
1502 pop si
1503 xor eax,eax
1504 jmp .prefix_done
1506 .gotprefix:
1507 pop cx ; Adjust stack
1508 inc si ; Skip double colon
1509 inc si
1511 .prefix_done:
1512 stosd ; Save IP address prefix
1513 mov cx,FILENAME_MAX-5
1515 .mn_loop:
1516 lodsb
1517 cmp al,' ' ; If control or space, end
1518 jna .mn_end
1519 stosb
1520 loop .mn_loop
1521 .mn_end:
1522 inc cx ; At least one null byte
1523 xor ax,ax ; Zero-fill name
1524 rep stosb ; Doesn't do anything if CX=0
1525 ret ; Done
1528 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1529 ; filename to the conventional representation. This is needed
1530 ; for the BOOT_IMAGE= parameter for the kernel.
1531 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1532 ; known to be shorter.
1534 ; DS:SI -> input mangled file name
1535 ; ES:DI -> output buffer
1537 ; On return, DI points to the first byte after the output name,
1538 ; which is set to a null byte.
1540 unmangle_name:
1541 push eax
1542 lodsd
1543 and eax,eax
1544 jz .noip
1545 call gendotquad
1546 mov ax,'::'
1547 stosw
1548 .noip:
1549 call strcpy
1550 dec di ; Point to final null byte
1551 pop eax
1555 ; pxenv
1557 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1558 ; calling convention. This is a separate local routine so
1559 ; we can hook special things from it if necessary. In particular,
1560 ; some PXE stacks seem to not like being invoked from anything but
1561 ; the initial stack, so humour it.
1564 pxenv:
1565 %if USE_PXE_PROVIDED_STACK == 0
1566 mov [cs:PXEStack],sp
1567 mov [cs:PXEStack+2],ss
1568 lss sp,[cs:InitStack]
1569 %endif
1570 .jump: call 0:pxe_thunk ; Default to calling the thunk
1571 %if USE_PXE_PROVIDED_STACK == 0
1572 lss sp,[cs:PXEStack]
1573 %endif
1574 cld ; Make sure DF <- 0
1577 ; Must be after function def due to NASM bug
1578 PXENVEntry equ pxenv.jump+1
1581 ; pxe_thunk
1583 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1584 ; calling convention (using the stack.)
1586 ; This is called as a far routine so that we can just stick it into
1587 ; the PXENVEntry variable.
1589 pxe_thunk: push es
1590 push di
1591 push bx
1592 .jump: call 0:0
1593 add sp,byte 6
1594 cmp ax,byte 1
1595 cmc ; Set CF unless ax == 0
1596 retf
1598 ; Must be after function def due to NASM bug
1599 PXEEntry equ pxe_thunk.jump+1
1602 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1604 ; In this case, get multiple blocks from a specific TCP connection.
1606 ; On entry:
1607 ; ES:BX -> Buffer
1608 ; SI -> TFTP socket pointer
1609 ; CX -> 512-byte block count; 0FFFFh = until end of file
1610 ; On exit:
1611 ; SI -> TFTP socket pointer (or 0 on EOF)
1612 ; CF = 1 -> Hit EOF
1614 getfssec: push si
1615 push fs
1616 mov di,bx
1617 mov bx,si
1618 mov ax,pktbuf_seg
1619 mov fs,ax
1621 movzx ecx,cx
1622 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1623 jz .hit_eof ; Nothing to do?
1625 .need_more:
1626 push ecx
1628 movzx eax,word [bx+tftp_bytesleft]
1629 cmp ecx,eax
1630 jna .ok_size
1631 mov ecx,eax
1632 jcxz .need_packet ; No bytes available?
1633 .ok_size:
1635 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1636 mov si,[bx+tftp_dataptr]
1637 sub [bx+tftp_bytesleft],cx
1638 fs rep movsb ; Copy from packet buffer
1639 mov [bx+tftp_dataptr],si
1641 pop ecx
1642 sub ecx,eax
1643 jnz .need_more
1646 .hit_eof:
1647 pop fs
1648 pop si
1650 ; Is there anything left of this?
1651 mov eax,[si+tftp_filesize]
1652 sub eax,[si+tftp_filepos]
1653 jnz .bytes_left ; CF <- 0
1655 cmp [si+tftp_bytesleft],ax
1656 jnz .bytes_left ; CF <- 0
1658 ; The socket is closed and the buffer drained
1659 ; Close socket structure and re-init for next user
1660 call free_socket
1662 .bytes_left:
1666 ; No data in buffer, check to see if we can get a packet...
1668 .need_packet:
1669 pop ecx
1670 mov eax,[bx+tftp_filesize]
1671 cmp eax,[bx+tftp_filepos]
1672 je .hit_eof ; Already EOF'd; socket already closed
1674 pushad
1675 push es
1676 mov si,bx
1677 call get_packet
1678 pop es
1679 popad
1681 jmp .need_more
1684 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1686 get_packet:
1687 mov ax,ds
1688 mov es,ax
1690 .packet_loop:
1691 ; Start by ACKing the previous packet; this should cause the
1692 ; next packet to be sent.
1693 mov cx,PKT_RETRY
1694 mov word [PktTimeout],PKT_TIMEOUT
1696 .send_ack: push cx ; <D> Retry count
1698 mov ax,[si+tftp_lastpkt]
1699 call ack_packet ; Send ACK
1701 ; We used to test the error code here, but sometimes
1702 ; PXE would return negative status even though we really
1703 ; did send the ACK. Now, just treat a failed send as
1704 ; a normally lost packet, and let it time out in due
1705 ; course of events.
1707 .send_ok: ; Now wait for packet.
1708 mov dx,[BIOS_timer] ; Get current time
1710 mov cx,[PktTimeout]
1711 .wait_data: push cx ; <E> Timeout
1712 push dx ; <F> Old time
1714 mov bx,[si+tftp_pktbuf]
1715 mov [pxe_udp_read_pkt.buffer],bx
1716 mov [pxe_udp_read_pkt.buffer+2],fs
1717 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1718 mov eax,[si+tftp_remoteip]
1719 mov [pxe_udp_read_pkt.sip],eax
1720 mov eax,[MyIP]
1721 mov [pxe_udp_read_pkt.dip],eax
1722 mov ax,[si+tftp_remoteport]
1723 mov [pxe_udp_read_pkt.rport],ax
1724 mov ax,[si+tftp_localport]
1725 mov [pxe_udp_read_pkt.lport],ax
1726 mov di,pxe_udp_read_pkt
1727 mov bx,PXENV_UDP_READ
1728 push si ; <G>
1729 call pxenv
1730 pop si ; <G>
1731 and ax,ax
1732 jz .recv_ok
1734 ; No packet, or receive failure
1735 mov dx,[BIOS_timer]
1736 pop ax ; <F> Old time
1737 pop cx ; <E> Timeout
1738 cmp ax,dx ; Same time -> don't advance timeout
1739 je .wait_data ; Same clock tick
1740 loop .wait_data ; Decrease timeout
1742 pop cx ; <D> Didn't get any, send another ACK
1743 shl word [PktTimeout],1 ; Exponential backoff
1744 loop .send_ack
1745 jmp kaboom ; Forget it...
1747 .recv_ok: pop dx ; <F>
1748 pop cx ; <E>
1750 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1751 jb .wait_data ; Bad size for a DATA packet
1753 mov bx,[si+tftp_pktbuf]
1754 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1755 jne .wait_data ; Then wait for something else
1757 mov ax,[si+tftp_lastpkt]
1758 xchg ah,al ; Host byte order
1759 inc ax ; Which packet are we waiting for?
1760 xchg ah,al ; Network byte order
1761 cmp [fs:bx+2],ax
1762 je .right_packet
1764 ; Wrong packet, ACK the packet and then try again
1765 ; This is presumably because the ACK got lost,
1766 ; so the server just resent the previous packet
1767 mov ax,[fs:bx+2]
1768 call ack_packet
1769 jmp .send_ok ; Reset timeout
1771 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1773 pop cx ; <D> Don't need the retry count anymore
1775 mov [si+tftp_lastpkt],ax ; Update last packet number
1777 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1778 sub cx,byte 4 ; Skip TFTP header
1780 ; If this is a zero-length block, don't mess with the pointers,
1781 ; since we may have just set up the previous block that way
1782 jz .last_block
1784 ; Set pointer to data block
1785 lea ax,[bx+4] ; Data past TFTP header
1786 mov [si+tftp_dataptr],ax
1788 add [si+tftp_filepos],ecx
1789 mov [si+tftp_bytesleft],cx
1791 cmp cx,[si+tftp_blksize] ; Is it a full block?
1792 jb .last_block ; If so, it's not EOF
1794 ; If we had the exact right number of bytes, always get
1795 ; one more packet to get the (zero-byte) EOF packet and
1796 ; close the socket.
1797 mov eax,[si+tftp_filepos]
1798 cmp [si+tftp_filesize],eax
1799 je .packet_loop
1804 .last_block: ; Last block - ACK packet immediately
1805 mov ax,[fs:bx+2]
1806 call ack_packet
1808 ; Make sure we know we are at end of file
1809 mov eax,[si+tftp_filepos]
1810 mov [si+tftp_filesize],eax
1815 ; ack_packet:
1817 ; Send ACK packet. This is a common operation and so is worth canning.
1819 ; Entry:
1820 ; SI = TFTP block
1821 ; AX = Packet # to ack (network byte order)
1822 ; Exit:
1823 ; ZF = 0 -> Error
1824 ; All registers preserved
1826 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1828 ack_packet:
1829 pushad
1830 mov [ack_packet_buf+2],ax ; Packet number to ack
1831 mov ax,[si]
1832 mov [pxe_udp_write_pkt.lport],ax
1833 mov ax,[si+tftp_remoteport]
1834 mov [pxe_udp_write_pkt.rport],ax
1835 mov eax,[si+tftp_remoteip]
1836 mov [pxe_udp_write_pkt.sip],eax
1837 xor eax,[MyIP]
1838 and eax,[Netmask]
1839 jz .nogw
1840 mov eax,[Gateway]
1841 .nogw:
1842 mov [pxe_udp_write_pkt.gip],eax
1843 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1844 mov [pxe_udp_write_pkt.buffersize], word 4
1845 mov di,pxe_udp_write_pkt
1846 mov bx,PXENV_UDP_WRITE
1847 call pxenv
1848 cmp ax,byte 0 ; ZF = 1 if write OK
1849 popad
1853 ; unload_pxe:
1855 ; This function unloads the PXE and UNDI stacks and unclaims
1856 ; the memory.
1858 unload_pxe:
1859 test byte [KeepPXE],01h ; Should we keep PXE around?
1860 jnz reset_pxe
1862 push ds
1863 push es
1865 mov ax,cs
1866 mov ds,ax
1867 mov es,ax
1869 mov si,new_api_unload
1870 cmp byte [APIVer+1],2 ; Major API version >= 2?
1871 jae .new_api
1872 mov si,old_api_unload
1873 .new_api:
1875 .call_loop: xor ax,ax
1876 lodsb
1877 and ax,ax
1878 jz .call_done
1879 xchg bx,ax
1880 mov di,pxe_unload_stack_pkt
1881 push di
1882 xor ax,ax
1883 mov cx,pxe_unload_stack_pkt_len >> 1
1884 rep stosw
1885 pop di
1886 call pxenv
1887 jc .cant_free
1888 mov ax,word [pxe_unload_stack_pkt.status]
1889 cmp ax,PXENV_STATUS_SUCCESS
1890 jne .cant_free
1891 jmp .call_loop
1893 .call_done:
1894 mov bx,0FF00h
1896 mov dx,[RealBaseMem]
1897 cmp dx,[BIOS_fbm] ; Sanity check
1898 jna .cant_free
1899 inc bx
1901 ; Check that PXE actually unhooked the INT 1Ah chain
1902 movzx eax,word [4*0x1a]
1903 movzx ecx,word [4*0x1a+2]
1904 shl ecx,4
1905 add eax,ecx
1906 shr eax,10
1907 cmp ax,dx ; Not in range
1908 jae .ok
1909 cmp ax,[BIOS_fbm]
1910 jae .cant_free
1911 ; inc bx
1913 .ok:
1914 mov [BIOS_fbm],dx
1915 .pop_ret:
1916 pop es
1917 pop ds
1920 .cant_free:
1921 mov si,cant_free_msg
1922 call writestr
1923 push ax
1924 xchg bx,ax
1925 call writehex4
1926 mov al,'-'
1927 call writechr
1928 pop ax
1929 call writehex4
1930 mov al,'-'
1931 call writechr
1932 mov eax,[4*0x1a]
1933 call writehex8
1934 call crlf
1935 jmp .pop_ret
1937 ; We want to keep PXE around, but still we should reset
1938 ; it to the standard bootup configuration
1939 reset_pxe:
1940 push es
1941 push cs
1942 pop es
1943 mov bx,PXENV_UDP_CLOSE
1944 mov di,pxe_udp_close_pkt
1945 call pxenv
1946 pop es
1950 ; gendotquad
1952 ; Take an IP address (in network byte order) in EAX and
1953 ; output a dotted quad string to ES:DI.
1954 ; DI points to terminal null at end of string on exit.
1956 gendotquad:
1957 push eax
1958 push cx
1959 mov cx,4
1960 .genchar:
1961 push eax
1962 cmp al,10 ; < 10?
1963 jb .lt10 ; If so, skip first 2 digits
1965 cmp al,100 ; < 100
1966 jb .lt100 ; If so, skip first digit
1968 aam 100
1969 ; Now AH = 100-digit; AL = remainder
1970 add ah,'0'
1971 mov [es:di],ah
1972 inc di
1974 .lt100:
1975 aam 10
1976 ; Now AH = 10-digit; AL = remainder
1977 add ah,'0'
1978 mov [es:di],ah
1979 inc di
1981 .lt10:
1982 add al,'0'
1983 stosb
1984 mov al,'.'
1985 stosb
1986 pop eax
1987 ror eax,8 ; Move next char into LSB
1988 loop .genchar
1989 dec di
1990 mov [es:di], byte 0
1991 pop cx
1992 pop eax
1996 ; parse_dhcp
1998 ; Parse a DHCP packet. This includes dealing with "overloaded"
1999 ; option fields (see RFC 2132, section 9.3)
2001 ; This should fill in the following global variables, if the
2002 ; information is present:
2004 ; MyIP - client IP address
2005 ; ServerIP - boot server IP address
2006 ; Netmask - network mask
2007 ; Gateway - default gateway router IP
2008 ; BootFile - boot file name
2009 ; DNSServers - DNS server IPs
2010 ; LocalDomain - Local domain name
2012 ; This assumes the DHCP packet is in "trackbuf" and the length
2013 ; of the packet in in CX on entry.
2016 parse_dhcp:
2017 mov byte [OverLoad],0 ; Assume no overload
2018 mov eax, [trackbuf+bootp.yip]
2019 and eax, eax
2020 jz .noyip
2021 cmp al,224 ; Class D or higher -> bad
2022 jae .noyip
2023 mov [MyIP], eax
2024 .noyip:
2025 mov eax, [trackbuf+bootp.sip]
2026 and eax, eax
2027 jz .nosip
2028 cmp al,224 ; Class D or higher -> bad
2029 jae .nosip
2030 mov [ServerIP], eax
2031 .nosip:
2032 sub cx, bootp.options
2033 jbe .nooptions
2034 mov si, trackbuf+bootp.option_magic
2035 lodsd
2036 cmp eax, BOOTP_OPTION_MAGIC
2037 jne .nooptions
2038 call parse_dhcp_options
2039 .nooptions:
2040 mov si, trackbuf+bootp.bootfile
2041 test byte [OverLoad],1
2042 jz .nofileoverload
2043 mov cx,128
2044 call parse_dhcp_options
2045 jmp short .parsed_file
2046 .nofileoverload:
2047 cmp byte [si], 0
2048 jz .parsed_file ; No bootfile name
2049 mov di,BootFile
2050 mov cx,32
2051 rep movsd
2052 xor al,al
2053 stosb ; Null-terminate
2054 .parsed_file:
2055 mov si, trackbuf+bootp.sname
2056 test byte [OverLoad],2
2057 jz .nosnameoverload
2058 mov cx,64
2059 call parse_dhcp_options
2060 .nosnameoverload:
2064 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2065 ; size is CX -- some DHCP servers leave option fields unterminated
2066 ; in violation of the spec.
2068 ; For parse_some_dhcp_options, DH contains the minimum value for
2069 ; the option to recognize -- this is used to restrict parsing to
2070 ; PXELINUX-specific options only.
2072 parse_dhcp_options:
2073 xor dx,dx
2075 parse_some_dhcp_options:
2076 .loop:
2077 and cx,cx
2078 jz .done
2080 lodsb
2081 dec cx
2082 jz .done ; Last byte; must be PAD, END or malformed
2083 cmp al, 0 ; PAD option
2084 je .loop
2085 cmp al,255 ; END option
2086 je .done
2088 ; Anything else will have a length field
2089 mov dl,al ; DL <- option number
2090 xor ax,ax
2091 lodsb ; AX <- option length
2092 dec cx
2093 sub cx,ax ; Decrement bytes left counter
2094 jb .done ; Malformed option: length > field size
2096 cmp dl,dh ; Is the option value valid?
2097 jb .opt_done
2099 cmp dl,1 ; SUBNET MASK option
2100 jne .not_subnet
2101 mov ebx,[si]
2102 mov [Netmask],ebx
2103 jmp .opt_done
2104 .not_subnet:
2106 cmp dl,3 ; ROUTER option
2107 jne .not_router
2108 mov ebx,[si]
2109 mov [Gateway],ebx
2110 jmp .opt_done
2111 .not_router:
2113 cmp dl,6 ; DNS SERVERS option
2114 jne .not_dns
2115 pusha
2116 mov cx,ax
2117 shr cx,2
2118 cmp cl,DNS_MAX_SERVERS
2119 jna .oklen
2120 mov cl,DNS_MAX_SERVERS
2121 .oklen:
2122 mov di,DNSServers
2123 rep movsd
2124 mov [LastDNSServer],di
2125 popa
2126 jmp .opt_done
2127 .not_dns:
2129 cmp dl,15 ; DNS LOCAL DOMAIN option
2130 jne .not_localdomain
2131 pusha
2132 mov bx,si
2133 add bx,ax
2134 xor ax,ax
2135 xchg [bx],al ; Zero-terminate option
2136 mov di,LocalDomain
2137 call dns_mangle ; Convert to DNS label set
2138 mov [bx],al ; Restore ending byte
2139 popa
2140 jmp .opt_done
2141 .not_localdomain:
2143 cmp dl,43 ; VENDOR ENCAPSULATED option
2144 jne .not_vendor
2145 pusha
2146 mov dh,208 ; Only recognize PXELINUX options
2147 mov cx,ax ; Length of option = max bytes to parse
2148 call parse_some_dhcp_options ; Parse recursive structure
2149 popa
2150 jmp .opt_done
2151 .not_vendor:
2153 cmp dl,52 ; OPTION OVERLOAD option
2154 jne .not_overload
2155 mov bl,[si]
2156 mov [OverLoad],bl
2157 jmp .opt_done
2158 .not_overload:
2160 cmp dl,67 ; BOOTFILE NAME option
2161 jne .not_bootfile
2162 mov di,BootFile
2163 jmp short .copyoption
2164 .done:
2165 ret ; This is here to make short jumps easier
2166 .not_bootfile:
2168 cmp dl,208 ; PXELINUX MAGIC option
2169 jne .not_pl_magic
2170 cmp al,4 ; Must have length == 4
2171 jne .opt_done
2172 cmp dword [si], htonl(0xF100747E) ; Magic number
2173 jne .opt_done
2174 or byte [DHCPMagic], byte 1 ; Found magic #
2175 jmp short .opt_done
2176 .not_pl_magic:
2178 cmp dl,209 ; PXELINUX CONFIGFILE option
2179 jne .not_pl_config
2180 mov di,ConfigName
2181 or byte [DHCPMagic], byte 2 ; Got config file
2182 jmp short .copyoption
2183 .not_pl_config:
2185 cmp dl,210 ; PXELINUX PATHPREFIX option
2186 jne .not_pl_prefix
2187 mov di,PathPrefix
2188 or byte [DHCPMagic], byte 4 ; Got path prefix
2189 jmp short .copyoption
2190 .not_pl_prefix:
2192 cmp dl,211 ; PXELINUX REBOOTTIME option
2193 jne .not_pl_timeout
2194 cmp al,4
2195 jne .opt_done
2196 mov ebx,[si]
2197 xchg bl,bh ; Convert to host byte order
2198 rol ebx,16
2199 xchg bl,bh
2200 mov [RebootTime],ebx
2201 or byte [DHCPMagic], byte 8 ; Got RebootTime
2202 ; jmp short .opt_done
2203 .not_pl_timeout:
2205 ; Unknown option. Skip to the next one.
2206 .opt_done:
2207 add si,ax
2208 .opt_done_noskip:
2209 jmp .loop
2211 ; Common code for copying an option verbatim
2212 .copyoption:
2213 xchg cx,ax
2214 rep movsb
2215 xchg cx,ax ; Now ax == 0
2216 stosb ; Null-terminate
2217 jmp short .opt_done_noskip
2220 ; genipopt
2222 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2223 ; option into IPOption based on a DHCP packet in trackbuf.
2224 ; Assumes CS == DS == ES.
2226 genipopt:
2227 pushad
2228 mov di,IPOption
2229 mov eax,'ip='
2230 stosd
2231 dec di
2232 mov eax,[MyIP]
2233 call gendotquad
2234 mov al,':'
2235 stosb
2236 mov eax,[ServerIP]
2237 call gendotquad
2238 mov al,':'
2239 stosb
2240 mov eax,[Gateway]
2241 call gendotquad
2242 mov al,':'
2243 stosb
2244 mov eax,[Netmask]
2245 call gendotquad ; Zero-terminates its output
2246 sub di,IPOption
2247 mov [IPOptionLen],di
2248 popad
2252 ; Call the receive loop while idle. This is done mostly so we can respond to
2253 ; ARP messages, but perhaps in the future this can be used to do network
2254 ; console.
2256 ; hpa sez: people using automatic control on the serial port get very
2257 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2258 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2259 ; passed since the last poll, and reset this when a character is
2260 ; received (RESET_IDLE).
2262 reset_idle:
2263 push ax
2264 mov ax,[cs:BIOS_timer]
2265 mov [cs:IdleTimer],ax
2266 pop ax
2269 check_for_arp:
2270 push ax
2271 mov ax,[cs:BIOS_timer]
2272 sub ax,[cs:IdleTimer]
2273 cmp ax,4
2274 pop ax
2275 jae .need_poll
2277 .need_poll: pushad
2278 push ds
2279 push es
2280 mov ax,cs
2281 mov ds,ax
2282 mov es,ax
2283 mov di,packet_buf
2284 mov [pxe_udp_read_pkt.status],al ; 0
2285 mov [pxe_udp_read_pkt.buffer],di
2286 mov [pxe_udp_read_pkt.buffer+2],ds
2287 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2288 mov eax,[MyIP]
2289 mov [pxe_udp_read_pkt.dip],eax
2290 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2291 mov di,pxe_udp_read_pkt
2292 mov bx,PXENV_UDP_READ
2293 call pxenv
2294 ; Ignore result...
2295 pop es
2296 pop ds
2297 popad
2298 RESET_IDLE
2301 ; -----------------------------------------------------------------------------
2302 ; Common modules
2303 ; -----------------------------------------------------------------------------
2305 %include "getc.inc" ; getc et al
2306 %include "conio.inc" ; Console I/O
2307 %include "writestr.inc" ; String output
2308 writestr equ cwritestr
2309 %include "writehex.inc" ; Hexadecimal output
2310 %include "parseconfig.inc" ; High-level config file handling
2311 %include "parsecmd.inc" ; Low-level config file handling
2312 %include "bcopy32.inc" ; 32-bit bcopy
2313 %include "loadhigh.inc" ; Load a file into high memory
2314 %include "font.inc" ; VGA font stuff
2315 %include "graphics.inc" ; VGA graphics
2316 %include "highmem.inc" ; High memory sizing
2317 %include "strcpy.inc" ; strcpy()
2318 %include "rawcon.inc" ; Console I/O w/o using the console functions
2319 %include "dnsresolv.inc" ; DNS resolver
2321 ; -----------------------------------------------------------------------------
2322 ; Begin data section
2323 ; -----------------------------------------------------------------------------
2325 section .data
2327 hextbl_lower db '0123456789abcdef'
2328 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2329 db CR, LF, 0
2330 boot_prompt db 'boot: ', 0
2331 wipe_char db BS, ' ', BS, 0
2332 err_notfound db 'Could not find kernel image: ',0
2333 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
2334 err_noram db 'It appears your computer has less than '
2335 asciidec dosram_k
2336 db 'K of low ("DOS")'
2337 db CR, LF
2338 db 'RAM. Linux needs at least this amount to boot. If you get'
2339 db CR, LF
2340 db 'this message in error, hold down the Ctrl key while'
2341 db CR, LF
2342 db 'booting, and I will take your word for it.', CR, LF, 0
2343 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
2344 err_noparm db 'Missing parameter in config file.', CR, LF, 0
2345 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
2346 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
2347 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
2348 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
2349 db CR, LF, 0
2350 err_notdos db ': attempted DOS system call', CR, LF, 0
2351 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
2352 err_bssimage db 'BSS images not supported.', CR, LF, 0
2353 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
2354 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2355 bailmsg equ err_bootfailed
2356 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2357 err_pxefailed db 'PXE API call failed, error ', 0
2358 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2359 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2360 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2361 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2362 apiver_str db 'PXE API version is ',0
2363 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2364 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2365 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2366 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2367 undi_data_msg db 'UNDI data segment at: ',0
2368 undi_data_len_msg db 'UNDI data segment size: ',0
2369 undi_code_msg db 'UNDI code segment at: ',0
2370 undi_code_len_msg db 'UNDI code segment size: ',0
2371 cant_free_msg db 'Failed to free base memory, error ', 0
2372 notfound_msg db 'not found', CR, LF, 0
2373 myipaddr_msg db 'My IP address seems to be ',0
2374 tftpprefix_msg db 'TFTP prefix: ', 0
2375 localboot_msg db 'Booting from local disk...', CR, LF, 0
2376 cmdline_msg db 'Command line: ', CR, LF, 0
2377 ready_msg db 'Ready.', CR, LF, 0
2378 trying_msg db 'Trying to load: ', 0
2379 crlfloading_msg db CR, LF ; Fall through
2380 loading_msg db 'Loading ', 0
2381 dotdot_msg db '.'
2382 dot_msg db '.', 0
2383 fourbs_msg db BS, BS, BS, BS, 0
2384 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
2385 crlf_msg db CR, LF
2386 null_msg db 0
2387 crff_msg db CR, FF, 0
2388 default_str db 'default', 0
2389 default_len equ ($-default_str)
2390 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2391 cfgprefix db 'pxelinux.cfg/' ; No final null!
2392 cfgprefix_len equ ($-cfgprefix)
2395 ; Command line options we'd like to take a look at
2397 ; mem= and vga= are handled as normal 32-bit integer values
2398 initrd_cmd db 'initrd='
2399 initrd_cmd_len equ $-initrd_cmd
2401 ; This one we make ourselves
2402 bootif_str db 'BOOTIF='
2403 bootif_str_len equ $-bootif_str
2405 ; Config file keyword table
2407 %include "keywords.inc"
2410 ; Extensions to search for (in *forward* order).
2411 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2413 align 4, db 0
2414 exten_table: db '.cbt' ; COMBOOT (specific)
2415 db '.0', 0, 0 ; PXE bootstrap program
2416 db '.com' ; COMBOOT (same as DOS)
2417 db '.c32' ; COM32
2418 exten_table_end:
2419 dd 0, 0 ; Need 8 null bytes here
2422 ; PXE unload sequences
2424 new_api_unload:
2425 db PXENV_UDP_CLOSE
2426 db PXENV_UNDI_SHUTDOWN
2427 db PXENV_UNLOAD_STACK
2428 db PXENV_STOP_UNDI
2429 db 0
2430 old_api_unload:
2431 db PXENV_UDP_CLOSE
2432 db PXENV_UNDI_SHUTDOWN
2433 db PXENV_UNLOAD_STACK
2434 db PXENV_UNDI_CLEANUP
2435 db 0
2438 ; PXE query packets partially filled in
2440 pxe_bootp_query_pkt_2:
2441 .status: dw 0 ; Status
2442 .packettype: dw 2 ; DHCPACK packet
2443 .buffersize: dw trackbufsize ; Packet size
2444 .buffer: dw trackbuf, 0 ; seg:off of buffer
2445 .bufferlimit: dw trackbufsize ; Unused
2447 pxe_bootp_query_pkt_3:
2448 .status: dw 0 ; Status
2449 .packettype: dw 3 ; Boot server packet
2450 .buffersize: dw trackbufsize ; Packet size
2451 .buffer: dw trackbuf, 0 ; seg:off of buffer
2452 .bufferlimit: dw trackbufsize ; Unused
2454 pxe_bootp_size_query_pkt:
2455 .status: dw 0 ; Status
2456 .packettype: dw 2 ; DHCPACK packet
2457 .buffersize: dw 0 ; Packet size
2458 .buffer: dw 0, 0 ; seg:off of buffer
2459 .bufferlimit: dw 0 ; Unused
2461 pxe_udp_open_pkt:
2462 .status: dw 0 ; Status
2463 .sip: dd 0 ; Source (our) IP
2465 pxe_udp_close_pkt:
2466 .status: dw 0 ; Status
2468 pxe_udp_write_pkt:
2469 .status: dw 0 ; Status
2470 .sip: dd 0 ; Server IP
2471 .gip: dd 0 ; Gateway IP
2472 .lport: dw 0 ; Local port
2473 .rport: dw 0 ; Remote port
2474 .buffersize: dw 0 ; Size of packet
2475 .buffer: dw 0, 0 ; seg:off of buffer
2477 pxe_udp_read_pkt:
2478 .status: dw 0 ; Status
2479 .sip: dd 0 ; Source IP
2480 .dip: dd 0 ; Destination (our) IP
2481 .rport: dw 0 ; Remote port
2482 .lport: dw 0 ; Local port
2483 .buffersize: dw 0 ; Max packet size
2484 .buffer: dw 0, 0 ; seg:off of buffer
2487 ; Misc initialized (data) variables
2489 alignb 4, db 0
2490 BaseStack dd StackBuf ; ESP of base stack
2491 dw 0 ; SS of base stack
2492 NextSocket dw 49152 ; Counter for allocating socket numbers
2493 KeepPXE db 0 ; Should PXE be kept around?
2496 ; TFTP commands
2498 tftp_tail db 'octet', 0 ; Octet mode
2499 tsize_str db 'tsize' ,0 ; Request size
2500 tsize_len equ ($-tsize_str)
2501 db '0', 0
2502 blksize_str db 'blksize', 0 ; Request large blocks
2503 blksize_len equ ($-blksize_str)
2504 asciidec TFTP_LARGEBLK
2505 db 0
2506 tftp_tail_len equ ($-tftp_tail)
2508 alignb 2, db 0
2510 ; Options negotiation parsing table (string pointer, string len, offset
2511 ; into socket structure)
2513 tftp_opt_table:
2514 dw tsize_str, tsize_len, tftp_filesize
2515 dw blksize_str, blksize_len, tftp_blksize
2516 tftp_opts equ ($-tftp_opt_table)/6
2519 ; Error packet to return on options negotiation error
2521 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2522 dw TFTP_EOPTNEG ; ERROR 8: bad options
2523 db 'tsize option required', 0 ; Error message
2524 tftp_opt_err_len equ ($-tftp_opt_err)
2526 alignb 4, db 0
2527 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2530 ; IP information (initialized to "unknown" values)
2531 MyIP dd 0 ; My IP address
2532 ServerIP dd 0 ; IP address of boot server
2533 Netmask dd 0 ; Netmask of this subnet
2534 Gateway dd 0 ; Default router
2535 ServerPort dw TFTP_PORT ; TFTP server port
2538 ; Variables that are uninitialized in SYSLINUX but initialized here
2540 alignb 4, db 0
2541 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2542 BufSafeSec dw trackbufsize/512 ; = how many sectors?
2543 BufSafeBytes dw trackbufsize ; = how many bytes?
2544 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
2545 %ifndef DEPEND
2546 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2547 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2548 %endif
2549 %endif
2550 IPAppend db 0 ; Default IPAPPEND option
2551 DHCPMagic db 0 ; DHCP site-specific option info