Adding upstream version 3.51.
[syslinux-debian/hramrach.git] / pxelinux.asm
blob003d910372b8ec6052786f9cae601162cc9665bc
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-2007 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 1440
45 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
46 ; Standard TFTP block size
47 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
48 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
49 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
51 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
52 SECTOR_SIZE equ TFTP_BLOCKSIZE
55 ; This is what we need to do when idle
56 ; *** This is disabled because some PXE stacks wait for unacceptably
57 ; *** long if there are no packets receivable.
59 %define HAVE_IDLE 0 ; idle is not a noop
61 %if HAVE_IDLE
62 %macro RESET_IDLE 0
63 call reset_idle
64 %endmacro
65 %macro DO_IDLE 0
66 call check_for_arp
67 %endmacro
68 %else
69 %macro RESET_IDLE 0
70 ; Nothing
71 %endmacro
72 %macro DO_IDLE 0
73 ; Nothing
74 %endmacro
75 %endif
78 ; TFTP operation codes
80 TFTP_RRQ equ htons(1) ; Read request
81 TFTP_WRQ equ htons(2) ; Write request
82 TFTP_DATA equ htons(3) ; Data packet
83 TFTP_ACK equ htons(4) ; ACK packet
84 TFTP_ERROR equ htons(5) ; ERROR packet
85 TFTP_OACK equ htons(6) ; OACK packet
88 ; TFTP error codes
90 TFTP_EUNDEF equ htons(0) ; Unspecified error
91 TFTP_ENOTFOUND equ htons(1) ; File not found
92 TFTP_EACCESS equ htons(2) ; Access violation
93 TFTP_ENOSPACE equ htons(3) ; Disk full
94 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
95 TFTP_EBADID equ htons(5) ; Unknown transfer
96 TFTP_EEXISTS equ htons(6) ; File exists
97 TFTP_ENOUSER equ htons(7) ; No such user
98 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
101 ; The following structure is used for "virtual kernels"; i.e. LILO-style
102 ; option labels. The options we permit here are `kernel' and `append
103 ; Since there is no room in the bottom 64K for all of these, we
104 ; stick them at vk_seg:0000 and copy them down before we need them.
106 struc vkernel
107 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
108 vk_rname: resb FILENAME_MAX ; Real name
109 vk_ipappend: resb 1 ; "IPAPPEND" flag
110 vk_type: resb 1 ; Type of file
111 vk_appendlen: resw 1
112 alignb 4
113 vk_append: resb max_cmd_len+1 ; Command line
114 alignb 4
115 vk_end: equ $ ; Should be <= vk_size
116 endstruc
119 ; Segment assignments in the bottom 640K
120 ; 0000h - main code/data segment (and BIOS segment)
122 real_mode_seg equ 4000h
123 pktbuf_seg equ 3000h ; Packet buffers segments
124 vk_seg equ 2000h ; Virtual kernels
125 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
126 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
129 ; BOOTP/DHCP packet pattern
131 struc bootp_t
132 bootp:
133 .opcode resb 1 ; BOOTP/DHCP "opcode"
134 .hardware resb 1 ; ARP hardware type
135 .hardlen resb 1 ; Hardware address length
136 .gatehops resb 1 ; Used by forwarders
137 .ident resd 1 ; Transaction ID
138 .seconds resw 1 ; Seconds elapsed
139 .flags resw 1 ; Broadcast flags
140 .cip resd 1 ; Client IP
141 .yip resd 1 ; "Your" IP
142 .sip resd 1 ; Next server IP
143 .gip resd 1 ; Relay agent IP
144 .macaddr resb 16 ; Client MAC address
145 .sname resb 64 ; Server name (optional)
146 .bootfile resb 128 ; Boot file name
147 .option_magic resd 1 ; Vendor option magic cookie
148 .options resb 1260 ; Vendor options
149 endstruc
151 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
154 ; TFTP connection data structure. Each one of these corresponds to a local
155 ; UDP port. The size of this structure must be a power of 2.
156 ; HBO = host byte order; NBO = network byte order
157 ; (*) = written by options negotiation code, must be dword sized
159 struc open_file_t
160 tftp_localport resw 1 ; Local port number (0 = not in use)
161 tftp_remoteport resw 1 ; Remote port number
162 tftp_remoteip resd 1 ; Remote IP address
163 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
164 tftp_filesize resd 1 ; Total file size(*)
165 tftp_blksize resd 1 ; Block size for this connection(*)
166 tftp_bytesleft resw 1 ; Unclaimed data bytes
167 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
168 tftp_dataptr resw 1 ; Pointer to available data
169 resw 2 ; Currently unusued
170 ; At end since it should not be zeroed on socked close
171 tftp_pktbuf resw 1 ; Packet buffer offset
172 endstruc
173 %ifndef DEPEND
174 %if (open_file_t_size & (open_file_t_size-1))
175 %error "open_file_t is not a power of 2"
176 %endif
177 %endif
179 ; ---------------------------------------------------------------------------
180 ; BEGIN CODE
181 ; ---------------------------------------------------------------------------
184 ; Memory below this point is reserved for the BIOS and the MBR
186 section .earlybss
187 trackbufsize equ 8192
188 trackbuf resb trackbufsize ; Track buffer goes here
189 getcbuf resb trackbufsize
190 ; ends at 4800h
192 ; Put some large buffers here, before RBFG_brainfuck,
193 ; where we can still carefully control the address
194 ; assignments...
196 alignb open_file_t_size
197 Files resb MAX_OPEN*open_file_t_size
199 alignb FILENAME_MAX
200 BootFile resb 256 ; Boot file from DHCP packet
201 PathPrefix resb 256 ; Path prefix derived from boot file
202 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
203 IPOption resb 80 ; ip= option buffer
204 InitStack resd 1 ; Pointer to reset stack (SS:SP)
205 PXEStack resd 1 ; Saved stack during PXE call
207 ; Warning here: RBFG build 22 randomly overwrites memory location
208 ; [0x5680,0x576c), possibly more. It seems that it gets confused and
209 ; screws up the pointer to its own internal packet buffer and starts
210 ; writing a received ARP packet into low memory.
211 RBFG_brainfuck resb 0E00h
213 section .latebss
214 alignb 4
215 RebootTime resd 1 ; Reboot timeout, if set by option
216 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
217 APIVer resw 1 ; PXE API version found
218 IPOptionLen resw 1 ; Length of IPOption
219 IdleTimer resw 1 ; Time to check for ARP?
220 LocalBootType resw 1 ; Local boot return code
221 PktTimeout resw 1 ; Timeout for current packet
222 RealBaseMem resw 1 ; Amount of DOS memory after freeing
223 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
224 DHCPMagic resb 1 ; PXELINUX magic flags
226 ; The relative position of these fields matter!
227 MAC_MAX equ 32 ; Handle hardware addresses this long
228 MACLen resb 1 ; MAC address len
229 MACType resb 1 ; MAC address type
230 MAC resb MAC_MAX+1 ; Actual MAC address
231 BOOTIFStr resb 7 ; Space for "BOOTIF="
232 MACStr resb 3*(MAC_MAX+1) ; MAC address as a string
234 ; The relative position of these fields matter!
235 UUIDType resb 1 ; Type byte from DHCP option
236 UUID resb 16 ; UUID, from the PXE stack
237 UUIDNull resb 1 ; dhcp_copyoption zero-terminates
240 ; PXE packets which don't need static initialization
242 alignb 4
243 pxe_unload_stack_pkt:
244 .status: resw 1 ; Status
245 .reserved: resw 10 ; Reserved
246 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
248 alignb 16
249 ; BOOTP/DHCP packet buffer
251 alignb 16
252 packet_buf resb 2048 ; Transfer packet
253 packet_buf_size equ $-packet_buf
256 ; Constants for the xfer_buf_seg
258 ; The xfer_buf_seg is also used to store message file buffers. We
259 ; need two trackbuffers (text and graphics), plus a work buffer
260 ; for the graphics decompressor.
262 xbs_textbuf equ 0 ; Also hard-coded, do not change
263 xbs_vgabuf equ trackbufsize
264 xbs_vgatmpbuf equ 2*trackbufsize
266 section .text
268 ; PXELINUX needs more BSS than the other derivatives;
269 ; therefore we relocate it from 7C00h on startup.
271 StackBuf equ $ ; Base of stack if we use our own
274 ; Primary entry point.
276 bootsec equ $
277 _start:
278 pushfd ; Paranoia... in case of return to PXE
279 pushad ; ... save as much state as possible
280 push ds
281 push es
282 push fs
283 push gs
285 xor ax,ax
286 mov ds,ax
287 mov es,ax
289 %ifndef DEPEND
290 %if TEXT_START != 0x7c00
291 ; This is uglier than it should be, but works around
292 ; some NASM 0.98.38 bugs.
293 mov di,section..bcopy32.start
294 add di,__bcopy_size-4
295 lea si,[di-(TEXT_START-7C00h)]
296 lea cx,[di-(TEXT_START-4)]
297 shr cx,2
298 std ; Overlapping areas, copy backwards
299 rep movsd
300 %endif
301 %endif
302 jmp 0:_start1 ; Canonicalize address
303 _start1:
304 mov bp,sp
305 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
307 ; That is all pushed onto the PXE stack. Save the pointer
308 ; to it and switch to an internal stack.
309 mov [InitStack],sp
310 mov [InitStack+2],ss
312 %if USE_PXE_PROVIDED_STACK
313 ; Apparently some platforms go bonkers if we
314 ; set up our own stack...
315 mov [BaseStack],sp
316 mov [BaseStack+4],ss
317 %endif
319 cli ; Paranoia
320 lss esp,[BaseStack]
322 sti ; Stack set up and ready
323 cld ; Copy upwards
326 ; Initialize screen (if we're using one)
328 push es ; Save ES -> PXE entry structure
329 push ds
330 pop es ; ES <- DS
331 %include "init.inc"
332 pop es ; Restore ES -> PXE entry structure
334 ; Tell the user we got this far
336 mov si,syslinux_banner
337 call writestr
339 mov si,copyright_str
340 call writestr
343 ; Assume API version 2.1, in case we find the !PXE structure without
344 ; finding the PXENV+ structure. This should really look at the Base
345 ; Code ROM ID structure in have_pxe, but this is adequate for now --
346 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
347 ; about higher versions than that.
349 mov word [APIVer],0201h
352 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
353 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
354 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
355 ; We should make that the second test, and not trash ES:BX...
357 cmp dword [es:bx], '!PXE'
358 je have_pxe
360 ; Uh-oh, not there... try plan B
361 mov ax, 5650h
362 %if USE_PXE_PROVIDED_STACK == 0
363 lss sp,[InitStack]
364 %endif
365 int 1Ah ; May trash regs
366 %if USE_PXE_PROVIDED_STACK == 0
367 lss esp,[BaseStack]
368 %endif
370 jc no_pxe
371 cmp ax,564Eh
372 jne no_pxe
374 ; Okay, that gave us the PXENV+ structure, find !PXE
375 ; structure from that (if available)
376 cmp dword [es:bx], 'PXEN'
377 jne no_pxe
378 cmp word [es:bx+4], 'V+'
379 je have_pxenv
381 ; Nothing there either. Last-ditch: scan memory
382 call memory_scan_for_pxe_struct ; !PXE scan
383 jnc have_pxe
384 call memory_scan_for_pxenv_struct ; PXENV+ scan
385 jnc have_pxenv
387 no_pxe: mov si,err_nopxe
388 call writestr
389 jmp kaboom
391 have_pxenv:
392 mov [StrucPtr],bx
393 mov [StrucPtr+2],es
395 mov si,found_pxenv
396 call writestr
398 mov si,apiver_str
399 call writestr
400 mov ax,[es:bx+6]
401 mov [APIVer],ax
402 call writehex4
403 call crlf
405 cmp ax,0201h ; API version 2.1 or higher
406 jb old_api
407 mov si,bx
408 mov ax,es
409 les bx,[es:bx+28h] ; !PXE structure pointer
410 cmp dword [es:bx],'!PXE'
411 je have_pxe
413 ; Nope, !PXE structure missing despite API 2.1+, or at least
414 ; the pointer is missing. Do a last-ditch attempt to find it.
415 call memory_scan_for_pxe_struct
416 jnc have_pxe
418 ; Otherwise, no dice, use PXENV+ structure
419 mov bx,si
420 mov es,ax
422 old_api: ; Need to use a PXENV+ structure
423 mov si,using_pxenv_msg
424 call writestr
426 mov eax,[es:bx+0Ah] ; PXE RM API
427 mov [PXENVEntry],eax
429 mov si,undi_data_msg
430 call writestr
431 mov ax,[es:bx+20h]
432 call writehex4
433 call crlf
434 mov si,undi_data_len_msg
435 call writestr
436 mov ax,[es:bx+22h]
437 call writehex4
438 call crlf
439 mov si,undi_code_msg
440 call writestr
441 mov ax,[es:bx+24h]
442 call writehex4
443 call crlf
444 mov si,undi_code_len_msg
445 call writestr
446 mov ax,[es:bx+26h]
447 call writehex4
448 call crlf
450 ; Compute base memory size from PXENV+ structure
451 xor esi,esi
452 movzx eax,word [es:bx+20h] ; UNDI data seg
453 cmp ax,[es:bx+24h] ; UNDI code seg
454 ja .use_data
455 mov ax,[es:bx+24h]
456 mov si,[es:bx+26h]
457 jmp short .combine
458 .use_data:
459 mov si,[es:bx+22h]
460 .combine:
461 shl eax,4
462 add eax,esi
463 shr eax,10 ; Convert to kilobytes
464 mov [RealBaseMem],ax
466 mov si,pxenventry_msg
467 call writestr
468 mov ax,[PXENVEntry+2]
469 call writehex4
470 mov al,':'
471 call writechr
472 mov ax,[PXENVEntry]
473 call writehex4
474 call crlf
475 jmp have_entrypoint
477 have_pxe:
478 mov [StrucPtr],bx
479 mov [StrucPtr+2],es
481 mov eax,[es:bx+10h]
482 mov [PXEEntry],eax
484 mov si,undi_data_msg
485 call writestr
486 mov eax,[es:bx+2Ah]
487 call writehex8
488 call crlf
489 mov si,undi_data_len_msg
490 call writestr
491 mov ax,[es:bx+2Eh]
492 call writehex4
493 call crlf
494 mov si,undi_code_msg
495 call writestr
496 mov ax,[es:bx+32h]
497 call writehex8
498 call crlf
499 mov si,undi_code_len_msg
500 call writestr
501 mov ax,[es:bx+36h]
502 call writehex4
503 call crlf
505 ; Compute base memory size from !PXE structure
506 xor esi,esi
507 mov eax,[es:bx+2Ah]
508 cmp eax,[es:bx+32h]
509 ja .use_data
510 mov eax,[es:bx+32h]
511 mov si,[es:bx+36h]
512 jmp short .combine
513 .use_data:
514 mov si,[es:bx+2Eh]
515 .combine:
516 add eax,esi
517 shr eax,10
518 mov [RealBaseMem],ax
520 mov si,pxeentry_msg
521 call writestr
522 mov ax,[PXEEntry+2]
523 call writehex4
524 mov al,':'
525 call writechr
526 mov ax,[PXEEntry]
527 call writehex4
528 call crlf
530 have_entrypoint:
531 push cs
532 pop es ; Restore CS == DS == ES
535 ; Network-specific initialization
537 xor ax,ax
538 mov [LocalDomain],al ; No LocalDomain received
541 ; The DHCP client identifiers are best gotten from the DHCPREQUEST
542 ; packet (query info 1).
544 query_bootp_1:
545 mov dl,1
546 call pxe_get_cached_info
547 call parse_dhcp
549 ; We don't use flags from the request packet, so
550 ; this is a good time to initialize DHCPMagic...
551 mov byte [DHCPMagic],0
554 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
555 ; address). This lives in the DHCPACK packet (query info 2).
557 query_bootp_2:
558 mov dl,2
559 call pxe_get_cached_info
560 call parse_dhcp ; Parse DHCP packet
562 ; Save away MAC address (assume this is in query info 2. If this
563 ; turns out to be problematic it might be better getting it from
564 ; the query info 1 packet.)
566 .save_mac:
567 movzx cx,byte [trackbuf+bootp.hardlen]
568 cmp cx,16
569 jna .mac_ok
570 xor cx,cx ; Bad hardware address length
571 .mac_ok:
572 mov [MACLen],cl
573 mov al,[trackbuf+bootp.hardware]
574 mov [MACType],al
575 mov si,trackbuf+bootp.macaddr
576 mov di,MAC
577 rep movsb
579 ; Enable this if we really need to zero-pad this field...
580 ; mov cx,MAC+MAC_MAX+1
581 ; sub cx,di
582 ; xor ax,ax
583 ; rep stosb
586 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
587 ; packet (query info 3).
589 mov dl,3
590 call pxe_get_cached_info
591 call parse_dhcp ; Parse DHCP packet
594 ; Generate the bootif string, and the hardware-based config string.
596 make_bootif_string:
597 mov si,bootif_str
598 mov di,BOOTIFStr
599 mov cx,bootif_str_len
600 rep movsb
602 movzx cx,byte [MACLen]
603 mov si,MACType
604 inc cx
605 .hexify_mac:
606 push cx
607 mov cl,1 ; CH == 0 already
608 call lchexbytes
609 mov al,'-'
610 stosb
611 pop cx
612 loop .hexify_mac
613 mov [di-1],cl ; Null-terminate and strip final dash
615 ; Generate ip= option
617 call genipopt
620 ; Print IP address
622 mov eax,[MyIP]
623 mov di,DotQuadBuf
624 push di
625 call gendotquad ; This takes network byte order input
627 xchg ah,al ; Convert to host byte order
628 ror eax,16 ; (BSWAP doesn't work on 386)
629 xchg ah,al
631 mov si,myipaddr_msg
632 call writestr
633 call writehex8
634 mov al,' '
635 call writechr
636 pop si ; DotQuadBuf
637 call writestr
638 call crlf
640 mov si,IPOption
641 call writestr
642 call crlf
645 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
646 ; if we didn't get the magic enable, do not recognize any other options.
648 check_dhcp_magic:
649 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
650 jnz .got_magic
651 mov byte [DHCPMagic], 0 ; If not, kill all other options
652 .got_magic:
656 ; Initialize UDP stack
658 udp_init:
659 mov eax,[MyIP]
660 mov [pxe_udp_open_pkt.sip],eax
661 mov di,pxe_udp_open_pkt
662 mov bx,PXENV_UDP_OPEN
663 call pxenv
664 jc .failed
665 cmp word [pxe_udp_open_pkt.status], byte 0
666 je .success
667 .failed: mov si,err_udpinit
668 call writestr
669 jmp kaboom
670 .success:
673 ; Common initialization code
675 %include "cpuinit.inc"
678 ; Now we're all set to start with our *real* business. First load the
679 ; configuration file (if any) and parse it.
681 ; In previous versions I avoided using 32-bit registers because of a
682 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
683 ; random. I figure, though, that if there are any of those still left
684 ; they probably won't be trying to install Linux on them...
686 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
687 ; to take'm out. In fact, we may want to put them back if we're going
688 ; to boot ELKS at some point.
692 ; Store standard filename prefix
694 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
695 jnz .got_prefix
696 mov si,BootFile
697 mov di,PathPrefix
699 call strcpy
700 mov cx,di
701 sub cx,PathPrefix+1
703 lea si,[di-2] ; Skip final null!
704 .find_alnum: lodsb
705 or al,20h
706 cmp al,'.' ; Count . or - as alphanum
707 je .alnum
708 cmp al,'-'
709 je .alnum
710 cmp al,'0'
711 jb .notalnum
712 cmp al,'9'
713 jbe .alnum
714 cmp al,'a'
715 jb .notalnum
716 cmp al,'z'
717 ja .notalnum
718 .alnum: loop .find_alnum
719 dec si
720 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
722 .got_prefix:
723 mov si,tftpprefix_msg
724 call writestr
725 mov si,PathPrefix
726 call writestr
727 call crlf
730 ; Load configuration file
732 find_config:
735 ; Begin looking for configuration file
737 config_scan:
738 test byte [DHCPMagic], 02h
739 jz .no_option
741 ; We got a DHCP option, try it first
742 call .try
743 jnz .success
745 .no_option:
746 mov di,ConfigName
747 mov si,cfgprefix
748 mov cx,cfgprefix_len
749 rep movsb
751 ; Have to guess config file name...
753 ; Try loading by UUID.
754 cmp byte [HaveUUID],0
755 je .no_uuid
757 push di
758 mov bx,uuid_dashes
759 mov si,UUID
760 .gen_uuid:
761 movzx cx,byte [bx]
762 jcxz .done_uuid
763 inc bx
764 call lchexbytes
765 mov al,'-'
766 stosb
767 jmp .gen_uuid
768 .done_uuid:
769 mov [di-1],cl ; Remove last dash and zero-terminate
770 pop di
771 call .try
772 jnz .success
773 .no_uuid:
775 ; Try loading by MAC address
776 push di
777 mov si,MACStr
778 call strcpy
779 pop di
780 call .try
781 jnz .success
783 ; Nope, try hexadecimal IP prefixes...
784 .scan_ip:
785 mov cx,4
786 mov si,MyIP
787 call uchexbytes ; Convert to hex string
789 mov cx,8 ; Up to 8 attempts
790 .tryagain:
791 mov byte [di],0 ; Zero-terminate string
792 call .try
793 jnz .success
794 dec di ; Drop one character
795 loop .tryagain
797 ; Final attempt: "default" string
798 mov si,default_str ; "default" string
799 call strcpy
800 call .try
801 jnz .success
803 mov si,err_noconfig
804 call writestr
805 jmp kaboom
807 .try:
808 pusha
809 mov si,trying_msg
810 call writestr
811 mov di,ConfigName
812 mov si,di
813 call writestr
814 call crlf
815 mov si,di
816 mov di,getcbuf
817 call mangle_name
818 call open
819 popa
823 .success:
826 ; Now we have the config file open. Parse the config file and
827 ; run the user interface.
829 %include "ui.inc"
832 ; Linux kernel loading code is common. However, we need to define
833 ; a couple of helper macros...
836 ; Handle "ipappend" option
837 %define HAVE_SPECIAL_APPEND
838 %macro SPECIAL_APPEND 0
839 test byte [IPAppend],01h ; ip=
840 jz .noipappend1
841 mov si,IPOption
842 mov cx,[IPOptionLen]
843 rep movsb
844 mov al,' '
845 stosb
846 .noipappend1:
847 test byte [IPAppend],02h
848 jz .noipappend2
849 mov si,BOOTIFStr
850 call strcpy
851 mov byte [es:di-1],' ' ; Replace null with space
852 .noipappend2:
853 %endmacro
855 ; Unload PXE stack
856 %define HAVE_UNLOAD_PREP
857 %macro UNLOAD_PREP 0
858 call unload_pxe
859 %endmacro
861 %include "runkernel.inc"
864 ; COMBOOT-loading code
866 %include "comboot.inc"
867 %include "com32.inc"
868 %include "cmdline.inc"
871 ; Boot sector loading code
873 %include "bootsect.inc"
876 ; Boot to the local disk by returning the appropriate PXE magic.
877 ; AX contains the appropriate return code.
879 local_boot:
880 push cs
881 pop ds
882 mov [LocalBootType],ax
883 call vgaclearmode
884 mov si,localboot_msg
885 call writestr
886 ; Restore the environment we were called with
887 lss sp,[InitStack]
888 pop gs
889 pop fs
890 pop es
891 pop ds
892 popad
893 mov ax,[cs:LocalBootType]
894 popfd
895 retf ; Return to PXE
898 ; Abort loading code
900 %include "abort.inc"
903 ; kaboom: write a message and bail out. Wait for quite a while,
904 ; or a user keypress, then do a hard reboot.
906 kaboom:
907 RESET_STACK_AND_SEGS AX
908 .patch: mov si,bailmsg
909 call writestr ; Returns with AL = 0
910 .drain: call pollchar
911 jz .drained
912 call getchar
913 jmp short .drain
914 .drained:
915 mov edi,[RebootTime]
916 mov al,[DHCPMagic]
917 and al,09h ; Magic+Timeout
918 cmp al,09h
919 je .time_set
920 mov edi,REBOOT_TIME
921 .time_set:
922 mov cx,18
923 .wait1: push cx
924 mov ecx,edi
925 .wait2: mov dx,[BIOS_timer]
926 .wait3: call pollchar
927 jnz .keypress
928 cmp dx,[BIOS_timer]
929 je .wait3
930 loop .wait2,ecx
931 mov al,'.'
932 call writechr
933 pop cx
934 loop .wait1
935 .keypress:
936 call crlf
937 mov word [BIOS_magic],0 ; Cold reboot
938 jmp 0F000h:0FFF0h ; Reset vector address
941 ; memory_scan_for_pxe_struct:
943 ; If none of the standard methods find the !PXE structure, look for it
944 ; by scanning memory.
946 ; On exit, if found:
947 ; CF = 0, ES:BX -> !PXE structure
948 ; Otherwise CF = 1, all registers saved
950 memory_scan_for_pxe_struct:
951 push ds
952 pusha
953 mov ax,cs
954 mov ds,ax
955 mov si,trymempxe_msg
956 call writestr
957 mov ax,[BIOS_fbm] ; Starting segment
958 shl ax,(10-4) ; Kilobytes -> paragraphs
959 ; mov ax,01000h ; Start to look here
960 dec ax ; To skip inc ax
961 .mismatch:
962 inc ax
963 cmp ax,0A000h ; End of memory
964 jae .not_found
965 call writehex4
966 mov si,fourbs_msg
967 call writestr
968 mov es,ax
969 mov edx,[es:0]
970 cmp edx,'!PXE'
971 jne .mismatch
972 movzx cx,byte [es:4] ; Length of structure
973 cmp cl,08h ; Minimum length
974 jb .mismatch
975 push ax
976 xor ax,ax
977 xor si,si
978 .checksum: es lodsb
979 add ah,al
980 loop .checksum
981 pop ax
982 jnz .mismatch ; Checksum must == 0
983 .found: mov bp,sp
984 xor bx,bx
985 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
986 mov ax,es
987 call writehex4
988 call crlf
989 popa
990 pop ds
993 .not_found: mov si,notfound_msg
994 call writestr
995 popa
996 pop ds
1001 ; memory_scan_for_pxenv_struct:
1003 ; If none of the standard methods find the PXENV+ structure, look for it
1004 ; by scanning memory.
1006 ; On exit, if found:
1007 ; CF = 0, ES:BX -> PXENV+ structure
1008 ; Otherwise CF = 1, all registers saved
1010 memory_scan_for_pxenv_struct:
1011 pusha
1012 mov si,trymempxenv_msg
1013 call writestr
1014 ; mov ax,[BIOS_fbm] ; Starting segment
1015 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1016 mov ax,01000h ; Start to look here
1017 dec ax ; To skip inc ax
1018 .mismatch:
1019 inc ax
1020 cmp ax,0A000h ; End of memory
1021 jae .not_found
1022 mov es,ax
1023 mov edx,[es:0]
1024 cmp edx,'PXEN'
1025 jne .mismatch
1026 mov dx,[es:4]
1027 cmp dx,'V+'
1028 jne .mismatch
1029 movzx cx,byte [es:8] ; Length of structure
1030 cmp cl,26h ; Minimum length
1031 jb .mismatch
1032 xor ax,ax
1033 xor si,si
1034 .checksum: es lodsb
1035 add ah,al
1036 loop .checksum
1037 and ah,ah
1038 jnz .mismatch ; Checksum must == 0
1039 .found: mov bp,sp
1040 mov [bp+8],bx ; Save BX into stack frame
1041 mov ax,bx
1042 call writehex4
1043 call crlf
1046 .not_found: mov si,notfound_msg
1047 call writestr
1048 popad
1053 ; close_file:
1054 ; Deallocates a file structure (pointer in SI)
1055 ; Assumes CS == DS.
1057 ; XXX: We should check to see if this file is still open on the server
1058 ; side and send a courtesy ERROR packet to the server.
1060 close_file:
1061 and si,si
1062 jz .closed
1063 mov word [si],0 ; Not in use
1064 .closed: ret
1067 ; searchdir:
1069 ; Open a TFTP connection to the server
1071 ; On entry:
1072 ; DS:DI = mangled filename
1073 ; If successful:
1074 ; ZF clear
1075 ; SI = socket pointer
1076 ; DX:AX = file length in bytes
1077 ; If unsuccessful
1078 ; ZF set
1081 searchdir:
1082 push es
1083 push bx
1084 push cx
1085 mov ax,ds
1086 mov es,ax
1087 mov si,di
1088 push bp
1089 mov bp,sp
1091 call allocate_socket
1092 jz .ret
1094 mov ax,PKT_RETRY ; Retry counter
1095 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1097 .sendreq: push ax ; [bp-2] - Retry counter
1098 push si ; [bp-4] - File name
1100 mov di,packet_buf
1101 mov [pxe_udp_write_pkt.buffer],di
1103 mov ax,TFTP_RRQ ; TFTP opcode
1104 stosw
1106 lodsd ; EAX <- server override (if any)
1107 and eax,eax
1108 jnz .noprefix ; No prefix, and we have the server
1110 push si ; Add common prefix
1111 mov si,PathPrefix
1112 call strcpy
1113 dec di
1114 pop si
1116 mov eax,[ServerIP] ; Get default server
1118 .noprefix:
1119 call strcpy ; Filename
1121 mov [bx+tftp_remoteip],eax
1123 push bx ; [bp-6] - TFTP block
1124 mov bx,[bx]
1125 push bx ; [bp-8] - TID (local port no)
1127 mov [pxe_udp_write_pkt.status],byte 0
1128 mov [pxe_udp_write_pkt.sip],eax
1129 ; Now figure out the gateway
1130 xor eax,[MyIP]
1131 and eax,[Netmask]
1132 jz .nogwneeded
1133 mov eax,[Gateway]
1134 .nogwneeded:
1135 mov [pxe_udp_write_pkt.gip],eax
1136 mov [pxe_udp_write_pkt.lport],bx
1137 mov ax,[ServerPort]
1138 mov [pxe_udp_write_pkt.rport],ax
1139 mov si,tftp_tail
1140 mov cx,tftp_tail_len
1141 rep movsb
1142 sub di,packet_buf ; Get packet size
1143 mov [pxe_udp_write_pkt.buffersize],di
1145 mov di,pxe_udp_write_pkt
1146 mov bx,PXENV_UDP_WRITE
1147 call pxenv
1148 jc .failure
1149 cmp word [pxe_udp_write_pkt.status],byte 0
1150 jne .failure
1153 ; Danger, Will Robinson! We need to support timeout
1154 ; and retry lest we just lost a packet...
1157 ; Packet transmitted OK, now we need to receive
1158 .getpacket: push word [PktTimeout] ; [bp-10]
1159 push word [BIOS_timer] ; [bp-12]
1161 .pkt_loop: mov bx,[bp-8] ; TID
1162 mov di,packet_buf
1163 mov word [pxe_udp_read_pkt.status],0
1164 mov [pxe_udp_read_pkt.buffer],di
1165 mov [pxe_udp_read_pkt.buffer+2],ds
1166 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1167 mov eax,[MyIP]
1168 mov [pxe_udp_read_pkt.dip],eax
1169 mov [pxe_udp_read_pkt.lport],bx
1170 mov di,pxe_udp_read_pkt
1171 mov bx,PXENV_UDP_READ
1172 call pxenv
1173 and ax,ax
1174 jz .got_packet ; Wait for packet
1175 .no_packet:
1176 mov dx,[BIOS_timer]
1177 cmp dx,[bp-12]
1178 je .pkt_loop
1179 mov [bp-12],dx
1180 dec word [bp-10] ; Timeout
1181 jnz .pkt_loop
1182 pop ax ; Adjust stack
1183 pop ax
1184 shl word [PktTimeout],1 ; Exponential backoff
1185 jmp .failure
1187 .got_packet:
1188 mov si,[bp-6] ; TFTP pointer
1189 mov bx,[bp-8] ; TID
1191 mov eax,[si+tftp_remoteip]
1192 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1193 jne .no_packet
1195 ; Got packet - reset timeout
1196 mov word [PktTimeout],PKT_TIMEOUT
1198 pop ax ; Adjust stack
1199 pop ax
1201 mov ax,[pxe_udp_read_pkt.rport]
1202 mov [si+tftp_remoteport],ax
1204 ; filesize <- -1 == unknown
1205 mov dword [si+tftp_filesize], -1
1206 ; Default blksize unless blksize option negotiated
1207 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1209 mov cx,[pxe_udp_read_pkt.buffersize]
1210 sub cx,2 ; CX <- bytes after opcode
1211 jb .failure ; Garbled reply
1213 mov si,packet_buf
1214 lodsw
1216 cmp ax, TFTP_ERROR
1217 je .bailnow ; ERROR reply: don't try again
1219 cmp ax, TFTP_OACK
1220 jne .no_tsize
1222 ; Now we need to parse the OACK packet to get the transfer
1223 ; size. SI -> first byte of options; CX -> byte count
1224 .parse_oack:
1225 jcxz .no_tsize ; No options acked
1226 .get_opt_name:
1227 mov di,si
1228 mov bx,si
1229 .opt_name_loop: lodsb
1230 and al,al
1231 jz .got_opt_name
1232 or al,20h ; Convert to lowercase
1233 stosb
1234 loop .opt_name_loop
1235 ; We ran out, and no final null
1236 jmp .err_reply
1237 .got_opt_name: ; si -> option value
1238 dec cx ; bytes left in pkt
1239 jz .err_reply ; Option w/o value
1241 ; Parse option pointed to by bx; guaranteed to be
1242 ; null-terminated.
1243 push cx
1244 push si
1245 mov si,bx ; -> option name
1246 mov bx,tftp_opt_table
1247 mov cx,tftp_opts
1248 .opt_loop:
1249 push cx
1250 push si
1251 mov di,[bx] ; Option pointer
1252 mov cx,[bx+2] ; Option len
1253 repe cmpsb
1254 pop si
1255 pop cx
1256 je .get_value ; OK, known option
1257 add bx,6
1258 loop .opt_loop
1260 pop si
1261 pop cx
1262 jmp .err_reply ; Non-negotiated option returned
1264 .get_value: pop si ; si -> option value
1265 pop cx ; cx -> bytes left in pkt
1266 mov bx,[bx+4] ; Pointer to data target
1267 add bx,[bp-6] ; TFTP socket pointer
1268 xor eax,eax
1269 xor edx,edx
1270 .value_loop: lodsb
1271 and al,al
1272 jz .got_value
1273 sub al,'0'
1274 cmp al, 9
1275 ja .err_reply ; Not a decimal digit
1276 imul edx,10
1277 add edx,eax
1278 mov [bx],edx
1279 loop .value_loop
1280 ; Ran out before final null, accept anyway
1281 jmp short .done_pkt
1283 .got_value:
1284 dec cx
1285 jnz .get_opt_name ; Not end of packet
1287 ; ZF == 1
1289 ; Success, done!
1290 .done_pkt:
1291 pop si ; Junk
1292 pop si ; We want the packet ptr in SI
1294 mov eax,[si+tftp_filesize]
1295 cmp eax,-1
1296 jz .no_tsize
1297 mov edx,eax
1298 shr edx,16 ; DX:AX == EAX
1300 and eax,eax ; Set ZF depending on file size
1301 pop bp ; Junk
1302 pop bp ; Junk (retry counter)
1303 jz .error_si ; ZF = 1 need to free the socket
1304 .ret:
1305 pop bp
1306 pop cx
1307 pop bx
1308 pop es
1311 .no_tsize:
1312 .err_reply: ; Option negotiation error. Send ERROR reply.
1313 ; ServerIP and gateway are already programmed in
1314 mov si,[bp-6]
1315 mov ax,[si+tftp_remoteport]
1316 mov word [pxe_udp_write_pkt.rport],ax
1317 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1318 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1319 mov di,pxe_udp_write_pkt
1320 mov bx,PXENV_UDP_WRITE
1321 call pxenv
1323 ; Write an error message and explode
1324 mov si,err_oldtftp
1325 call writestr
1326 jmp kaboom
1328 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1330 .failure: pop bx ; Junk
1331 pop bx
1332 pop si
1333 pop ax
1334 dec ax ; Retry counter
1335 jnz .sendreq ; Try again
1337 .error: mov si,bx ; Socket pointer
1338 .error_si: ; Socket pointer already in SI
1339 call free_socket ; ZF <- 1, SI <- 0
1340 jmp .ret
1343 ; allocate_socket: Allocate a local UDP port structure
1345 ; If successful:
1346 ; ZF set
1347 ; BX = socket pointer
1348 ; If unsuccessful:
1349 ; ZF clear
1351 allocate_socket:
1352 push cx
1353 mov bx,Files
1354 mov cx,MAX_OPEN
1355 .check: cmp word [bx], byte 0
1356 je .found
1357 add bx,open_file_t_size
1358 loop .check
1359 xor cx,cx ; ZF = 1
1360 pop cx
1362 ; Allocate a socket number. Socket numbers are made
1363 ; guaranteed unique by including the socket slot number
1364 ; (inverted, because we use the loop counter cx); add a
1365 ; counter value to keep the numbers from being likely to
1366 ; get immediately reused.
1368 ; The NextSocket variable also contains the top two bits
1369 ; set. This generates a value in the range 49152 to
1370 ; 57343.
1371 .found:
1372 dec cx
1373 push ax
1374 mov ax,[NextSocket]
1375 inc ax
1376 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1377 mov [NextSocket],ax
1378 shl cx,13-MAX_OPEN_LG2
1379 add cx,ax ; ZF = 0
1380 xchg ch,cl ; Convert to network byte order
1381 mov [bx],cx ; Socket in use
1382 pop ax
1383 pop cx
1387 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1389 free_socket:
1390 push es
1391 pusha
1392 xor ax,ax
1393 mov es,ax
1394 mov di,si
1395 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1396 rep stosw
1397 popa
1398 pop es
1399 xor si,si
1403 ; parse_dotquad:
1404 ; Read a dot-quad pathname in DS:SI and output an IP
1405 ; address in EAX, with SI pointing to the first
1406 ; nonmatching character.
1408 ; Return CF=1 on error.
1410 parse_dotquad:
1411 push cx
1412 mov cx,4
1413 xor eax,eax
1414 .parseloop:
1415 mov ch,ah
1416 mov ah,al
1417 lodsb
1418 sub al,'0'
1419 jb .notnumeric
1420 cmp al,9
1421 ja .notnumeric
1422 aad ; AL += 10 * AH; AH = 0;
1423 xchg ah,ch
1424 jmp .parseloop
1425 .notnumeric:
1426 cmp al,'.'-'0'
1427 pushf
1428 mov al,ah
1429 mov ah,ch
1430 xor ch,ch
1431 ror eax,8
1432 popf
1433 jne .error
1434 loop .parseloop
1435 jmp .done
1436 .error:
1437 loop .realerror ; If CX := 1 then we're done
1439 jmp .done
1440 .realerror:
1442 .done:
1443 dec si ; CF unchanged!
1444 pop cx
1447 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1448 ; to by ES:DI; ends on encountering any whitespace.
1449 ; DI is preserved.
1451 ; This verifies that a filename is < FILENAME_MAX characters
1452 ; and doesn't contain whitespace, and zero-pads the output buffer,
1453 ; so "repe cmpsb" can do a compare.
1455 ; The first four bytes of the manged name is the IP address of
1456 ; the download host.
1458 mangle_name:
1459 push di
1460 push si
1461 mov eax,[ServerIP]
1462 cmp byte [si],0
1463 je .noip ; Null filename?!?!
1464 cmp word [si],'::' ; Leading ::?
1465 je .gotprefix
1467 .more:
1468 inc si
1469 cmp byte [si],0
1470 je .noip
1471 cmp word [si],'::'
1472 jne .more
1474 ; We have a :: prefix of some sort, it could be either
1475 ; a DNS name or a dot-quad IP address. Try the dot-quad
1476 ; first...
1477 .here:
1478 pop si
1479 push si
1480 call parse_dotquad
1481 jc .notdq
1482 cmp word [si],'::'
1483 je .gotprefix
1484 .notdq:
1485 pop si
1486 push si
1487 call dns_resolv
1488 cmp word [si],'::'
1489 jne .noip
1490 and eax,eax
1491 jnz .gotprefix
1493 .noip:
1494 pop si
1495 xor eax,eax
1496 jmp .prefix_done
1498 .gotprefix:
1499 pop cx ; Adjust stack
1500 inc si ; Skip double colon
1501 inc si
1503 .prefix_done:
1504 stosd ; Save IP address prefix
1505 mov cx,FILENAME_MAX-5
1507 .mn_loop:
1508 lodsb
1509 cmp al,' ' ; If control or space, end
1510 jna .mn_end
1511 stosb
1512 loop .mn_loop
1513 .mn_end:
1514 inc cx ; At least one null byte
1515 xor ax,ax ; Zero-fill name
1516 rep stosb ; Doesn't do anything if CX=0
1517 pop di
1518 ret ; Done
1521 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1522 ; filename to the conventional representation. This is needed
1523 ; for the BOOT_IMAGE= parameter for the kernel.
1524 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1525 ; known to be shorter.
1527 ; DS:SI -> input mangled file name
1528 ; ES:DI -> output buffer
1530 ; On return, DI points to the first byte after the output name,
1531 ; which is set to a null byte.
1533 unmangle_name:
1534 push eax
1535 lodsd
1536 and eax,eax
1537 jz .noip
1538 call gendotquad
1539 mov ax,'::'
1540 stosw
1541 .noip:
1542 call strcpy
1543 dec di ; Point to final null byte
1544 pop eax
1548 ; pxenv
1550 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1551 ; calling convention. This is a separate local routine so
1552 ; we can hook special things from it if necessary. In particular,
1553 ; some PXE stacks seem to not like being invoked from anything but
1554 ; the initial stack, so humour it.
1557 pxenv:
1558 %if USE_PXE_PROVIDED_STACK == 0
1559 mov [cs:PXEStack],sp
1560 mov [cs:PXEStack+2],ss
1561 lss sp,[cs:InitStack]
1562 %endif
1563 .jump: call 0:pxe_thunk ; Default to calling the thunk
1564 %if USE_PXE_PROVIDED_STACK == 0
1565 lss sp,[cs:PXEStack]
1566 %endif
1567 cld ; Make sure DF <- 0
1570 ; Must be after function def due to NASM bug
1571 PXENVEntry equ pxenv.jump+1
1574 ; pxe_thunk
1576 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1577 ; calling convention (using the stack.)
1579 ; This is called as a far routine so that we can just stick it into
1580 ; the PXENVEntry variable.
1582 pxe_thunk: push es
1583 push di
1584 push bx
1585 .jump: call 0:0
1586 add sp,byte 6
1587 cmp ax,byte 1
1588 cmc ; Set CF unless ax == 0
1589 retf
1591 ; Must be after function def due to NASM bug
1592 PXEEntry equ pxe_thunk.jump+1
1595 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1597 ; In this case, get multiple blocks from a specific TCP connection.
1599 ; On entry:
1600 ; ES:BX -> Buffer
1601 ; SI -> TFTP socket pointer
1602 ; CX -> 512-byte block count; 0FFFFh = until end of file
1603 ; On exit:
1604 ; SI -> TFTP socket pointer (or 0 on EOF)
1605 ; CF = 1 -> Hit EOF
1607 getfssec: push si
1608 push fs
1609 mov di,bx
1610 mov bx,si
1611 mov ax,pktbuf_seg
1612 mov fs,ax
1614 movzx ecx,cx
1615 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1616 jz .hit_eof ; Nothing to do?
1618 .need_more:
1619 push ecx
1621 movzx eax,word [bx+tftp_bytesleft]
1622 cmp ecx,eax
1623 jna .ok_size
1624 mov ecx,eax
1625 jcxz .need_packet ; No bytes available?
1626 .ok_size:
1628 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1629 mov si,[bx+tftp_dataptr]
1630 sub [bx+tftp_bytesleft],cx
1631 fs rep movsb ; Copy from packet buffer
1632 mov [bx+tftp_dataptr],si
1634 pop ecx
1635 sub ecx,eax
1636 jnz .need_more
1639 .hit_eof:
1640 pop fs
1641 pop si
1643 ; Is there anything left of this?
1644 mov eax,[si+tftp_filesize]
1645 sub eax,[si+tftp_filepos]
1646 jnz .bytes_left ; CF <- 0
1648 cmp [si+tftp_bytesleft],ax
1649 jnz .bytes_left ; CF <- 0
1651 ; The socket is closed and the buffer drained
1652 ; Close socket structure and re-init for next user
1653 call free_socket
1655 .bytes_left:
1659 ; No data in buffer, check to see if we can get a packet...
1661 .need_packet:
1662 pop ecx
1663 mov eax,[bx+tftp_filesize]
1664 cmp eax,[bx+tftp_filepos]
1665 je .hit_eof ; Already EOF'd; socket already closed
1667 pushad
1668 push es
1669 mov si,bx
1670 call get_packet
1671 pop es
1672 popad
1674 jmp .need_more
1677 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1679 get_packet:
1680 mov ax,ds
1681 mov es,ax
1683 .packet_loop:
1684 ; Start by ACKing the previous packet; this should cause the
1685 ; next packet to be sent.
1686 mov cx,PKT_RETRY
1687 mov word [PktTimeout],PKT_TIMEOUT
1689 .send_ack: push cx ; <D> Retry count
1691 mov ax,[si+tftp_lastpkt]
1692 call ack_packet ; Send ACK
1694 ; We used to test the error code here, but sometimes
1695 ; PXE would return negative status even though we really
1696 ; did send the ACK. Now, just treat a failed send as
1697 ; a normally lost packet, and let it time out in due
1698 ; course of events.
1700 .send_ok: ; Now wait for packet.
1701 mov dx,[BIOS_timer] ; Get current time
1703 mov cx,[PktTimeout]
1704 .wait_data: push cx ; <E> Timeout
1705 push dx ; <F> Old time
1707 mov bx,[si+tftp_pktbuf]
1708 mov [pxe_udp_read_pkt.buffer],bx
1709 mov [pxe_udp_read_pkt.buffer+2],fs
1710 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1711 mov eax,[si+tftp_remoteip]
1712 mov [pxe_udp_read_pkt.sip],eax
1713 mov eax,[MyIP]
1714 mov [pxe_udp_read_pkt.dip],eax
1715 mov ax,[si+tftp_remoteport]
1716 mov [pxe_udp_read_pkt.rport],ax
1717 mov ax,[si+tftp_localport]
1718 mov [pxe_udp_read_pkt.lport],ax
1719 mov di,pxe_udp_read_pkt
1720 mov bx,PXENV_UDP_READ
1721 push si ; <G>
1722 call pxenv
1723 pop si ; <G>
1724 and ax,ax
1725 jz .recv_ok
1727 ; No packet, or receive failure
1728 mov dx,[BIOS_timer]
1729 pop ax ; <F> Old time
1730 pop cx ; <E> Timeout
1731 cmp ax,dx ; Same time -> don't advance timeout
1732 je .wait_data ; Same clock tick
1733 loop .wait_data ; Decrease timeout
1735 pop cx ; <D> Didn't get any, send another ACK
1736 shl word [PktTimeout],1 ; Exponential backoff
1737 loop .send_ack
1738 jmp kaboom ; Forget it...
1740 .recv_ok: pop dx ; <F>
1741 pop cx ; <E>
1743 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1744 jb .wait_data ; Bad size for a DATA packet
1746 mov bx,[si+tftp_pktbuf]
1747 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1748 jne .wait_data ; Then wait for something else
1750 mov ax,[si+tftp_lastpkt]
1751 xchg ah,al ; Host byte order
1752 inc ax ; Which packet are we waiting for?
1753 xchg ah,al ; Network byte order
1754 cmp [fs:bx+2],ax
1755 je .right_packet
1757 ; Wrong packet, ACK the packet and then try again
1758 ; This is presumably because the ACK got lost,
1759 ; so the server just resent the previous packet
1760 mov ax,[fs:bx+2]
1761 call ack_packet
1762 jmp .send_ok ; Reset timeout
1764 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1766 pop cx ; <D> Don't need the retry count anymore
1768 mov [si+tftp_lastpkt],ax ; Update last packet number
1770 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1771 sub cx,byte 4 ; Skip TFTP header
1773 ; If this is a zero-length block, don't mess with the pointers,
1774 ; since we may have just set up the previous block that way
1775 jz .last_block
1777 ; Set pointer to data block
1778 lea ax,[bx+4] ; Data past TFTP header
1779 mov [si+tftp_dataptr],ax
1781 add [si+tftp_filepos],ecx
1782 mov [si+tftp_bytesleft],cx
1784 cmp cx,[si+tftp_blksize] ; Is it a full block?
1785 jb .last_block ; If so, it's not EOF
1787 ; If we had the exact right number of bytes, always get
1788 ; one more packet to get the (zero-byte) EOF packet and
1789 ; close the socket.
1790 mov eax,[si+tftp_filepos]
1791 cmp [si+tftp_filesize],eax
1792 je .packet_loop
1797 .last_block: ; Last block - ACK packet immediately
1798 mov ax,[fs:bx+2]
1799 call ack_packet
1801 ; Make sure we know we are at end of file
1802 mov eax,[si+tftp_filepos]
1803 mov [si+tftp_filesize],eax
1808 ; ack_packet:
1810 ; Send ACK packet. This is a common operation and so is worth canning.
1812 ; Entry:
1813 ; SI = TFTP block
1814 ; AX = Packet # to ack (network byte order)
1815 ; Exit:
1816 ; ZF = 0 -> Error
1817 ; All registers preserved
1819 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1821 ack_packet:
1822 pushad
1823 mov [ack_packet_buf+2],ax ; Packet number to ack
1824 mov ax,[si]
1825 mov [pxe_udp_write_pkt.lport],ax
1826 mov ax,[si+tftp_remoteport]
1827 mov [pxe_udp_write_pkt.rport],ax
1828 mov eax,[si+tftp_remoteip]
1829 mov [pxe_udp_write_pkt.sip],eax
1830 xor eax,[MyIP]
1831 and eax,[Netmask]
1832 jz .nogw
1833 mov eax,[Gateway]
1834 .nogw:
1835 mov [pxe_udp_write_pkt.gip],eax
1836 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1837 mov [pxe_udp_write_pkt.buffersize], word 4
1838 mov di,pxe_udp_write_pkt
1839 mov bx,PXENV_UDP_WRITE
1840 call pxenv
1841 cmp ax,byte 0 ; ZF = 1 if write OK
1842 popad
1846 ; unload_pxe:
1848 ; This function unloads the PXE and UNDI stacks and unclaims
1849 ; the memory.
1851 unload_pxe:
1852 test byte [KeepPXE],01h ; Should we keep PXE around?
1853 jnz reset_pxe
1855 push ds
1856 push es
1858 mov ax,cs
1859 mov ds,ax
1860 mov es,ax
1862 mov si,new_api_unload
1863 cmp byte [APIVer+1],2 ; Major API version >= 2?
1864 jae .new_api
1865 mov si,old_api_unload
1866 .new_api:
1868 .call_loop: xor ax,ax
1869 lodsb
1870 and ax,ax
1871 jz .call_done
1872 xchg bx,ax
1873 mov di,pxe_unload_stack_pkt
1874 push di
1875 xor ax,ax
1876 mov cx,pxe_unload_stack_pkt_len >> 1
1877 rep stosw
1878 pop di
1879 call pxenv
1880 jc .cant_free
1881 mov ax,word [pxe_unload_stack_pkt.status]
1882 cmp ax,PXENV_STATUS_SUCCESS
1883 jne .cant_free
1884 jmp .call_loop
1886 .call_done:
1887 mov bx,0FF00h
1889 mov dx,[RealBaseMem]
1890 cmp dx,[BIOS_fbm] ; Sanity check
1891 jna .cant_free
1892 inc bx
1894 ; Check that PXE actually unhooked the INT 1Ah chain
1895 movzx eax,word [4*0x1a]
1896 movzx ecx,word [4*0x1a+2]
1897 shl ecx,4
1898 add eax,ecx
1899 shr eax,10
1900 cmp ax,dx ; Not in range
1901 jae .ok
1902 cmp ax,[BIOS_fbm]
1903 jae .cant_free
1904 ; inc bx
1906 .ok:
1907 mov [BIOS_fbm],dx
1908 .pop_ret:
1909 pop es
1910 pop ds
1913 .cant_free:
1914 mov si,cant_free_msg
1915 call writestr
1916 push ax
1917 xchg bx,ax
1918 call writehex4
1919 mov al,'-'
1920 call writechr
1921 pop ax
1922 call writehex4
1923 mov al,'-'
1924 call writechr
1925 mov eax,[4*0x1a]
1926 call writehex8
1927 call crlf
1928 jmp .pop_ret
1930 ; We want to keep PXE around, but still we should reset
1931 ; it to the standard bootup configuration
1932 reset_pxe:
1933 push es
1934 push cs
1935 pop es
1936 mov bx,PXENV_UDP_CLOSE
1937 mov di,pxe_udp_close_pkt
1938 call pxenv
1939 pop es
1943 ; gendotquad
1945 ; Take an IP address (in network byte order) in EAX and
1946 ; output a dotted quad string to ES:DI.
1947 ; DI points to terminal null at end of string on exit.
1949 gendotquad:
1950 push eax
1951 push cx
1952 mov cx,4
1953 .genchar:
1954 push eax
1955 cmp al,10 ; < 10?
1956 jb .lt10 ; If so, skip first 2 digits
1958 cmp al,100 ; < 100
1959 jb .lt100 ; If so, skip first digit
1961 aam 100
1962 ; Now AH = 100-digit; AL = remainder
1963 add ah,'0'
1964 mov [es:di],ah
1965 inc di
1967 .lt100:
1968 aam 10
1969 ; Now AH = 10-digit; AL = remainder
1970 add ah,'0'
1971 mov [es:di],ah
1972 inc di
1974 .lt10:
1975 add al,'0'
1976 stosb
1977 mov al,'.'
1978 stosb
1979 pop eax
1980 ror eax,8 ; Move next char into LSB
1981 loop .genchar
1982 dec di
1983 mov [es:di], byte 0
1984 pop cx
1985 pop eax
1988 ; uchexbytes/lchexbytes
1990 ; Take a number of bytes in memory and convert to upper/lower-case
1991 ; hexadecimal
1993 ; Input:
1994 ; DS:SI = input bytes
1995 ; ES:DI = output buffer
1996 ; CX = number of bytes
1997 ; Output:
1998 ; DS:SI = first byte after
1999 ; ES:DI = first byte after
2000 ; CX = 0
2002 ; Trashes AX, DX
2005 lchexbytes:
2006 mov dl,'a'-'9'-1
2007 jmp xchexbytes
2008 uchexbytes:
2009 mov dl,'A'-'9'-1
2010 xchexbytes:
2011 .loop:
2012 lodsb
2013 mov ah,al
2014 shr al,4
2015 call .outchar
2016 mov al,ah
2017 call .outchar
2018 loop .loop
2020 .outchar:
2021 and al,0Fh
2022 add al,'0'
2023 cmp al,'9'
2024 jna .done
2025 add al,dl
2026 .done:
2027 stosb
2031 ; pxe_get_cached_info
2033 ; Get a DHCP packet from the PXE stack into the trackbuf.
2035 ; Input:
2036 ; DL = packet type
2037 ; Output:
2038 ; CX = buffer size
2040 ; Assumes CS == DS == ES.
2042 pxe_get_cached_info:
2043 pushad
2044 mov di,pxe_bootp_query_pkt
2045 push di
2046 xor ax,ax
2047 stosw ; Status
2048 movzx ax,dl
2049 stosw ; Packet type
2050 mov ax,trackbufsize
2051 stosw ; Buffer size
2052 mov ax,trackbuf
2053 stosw ; Buffer offset
2054 xor ax,ax
2055 stosw ; Buffer segment
2057 pop di ; DI -> parameter set
2058 mov bx,PXENV_GET_CACHED_INFO
2059 call pxenv
2060 jc .err
2061 and ax,ax
2062 jnz .err
2064 popad
2065 mov cx,[pxe_bootp_query_pkt.buffersize]
2068 .err:
2069 mov si,err_pxefailed
2070 jmp kaboom
2073 ; parse_dhcp
2075 ; Parse a DHCP packet. This includes dealing with "overloaded"
2076 ; option fields (see RFC 2132, section 9.3)
2078 ; This should fill in the following global variables, if the
2079 ; information is present:
2081 ; MyIP - client IP address
2082 ; ServerIP - boot server IP address
2083 ; Netmask - network mask
2084 ; Gateway - default gateway router IP
2085 ; BootFile - boot file name
2086 ; DNSServers - DNS server IPs
2087 ; LocalDomain - Local domain name
2088 ; MACLen, MAC - Client identifier, if MACLen == 0
2090 ; This assumes the DHCP packet is in "trackbuf" and the length
2091 ; of the packet in in CX on entry.
2094 parse_dhcp:
2095 mov byte [OverLoad],0 ; Assume no overload
2096 mov eax, [trackbuf+bootp.yip]
2097 and eax, eax
2098 jz .noyip
2099 cmp al,224 ; Class D or higher -> bad
2100 jae .noyip
2101 mov [MyIP], eax
2102 .noyip:
2103 mov eax, [trackbuf+bootp.sip]
2104 and eax, eax
2105 jz .nosip
2106 cmp al,224 ; Class D or higher -> bad
2107 jae .nosip
2108 mov [ServerIP], eax
2109 .nosip:
2110 sub cx, bootp.options
2111 jbe .nooptions
2112 mov si, trackbuf+bootp.option_magic
2113 lodsd
2114 cmp eax, BOOTP_OPTION_MAGIC
2115 jne .nooptions
2116 call parse_dhcp_options
2117 .nooptions:
2118 mov si, trackbuf+bootp.bootfile
2119 test byte [OverLoad],1
2120 jz .nofileoverload
2121 mov cx,128
2122 call parse_dhcp_options
2123 jmp short .parsed_file
2124 .nofileoverload:
2125 cmp byte [si], 0
2126 jz .parsed_file ; No bootfile name
2127 mov di,BootFile
2128 mov cx,32
2129 rep movsd
2130 xor al,al
2131 stosb ; Null-terminate
2132 .parsed_file:
2133 mov si, trackbuf+bootp.sname
2134 test byte [OverLoad],2
2135 jz .nosnameoverload
2136 mov cx,64
2137 call parse_dhcp_options
2138 .nosnameoverload:
2142 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2143 ; size is CX -- some DHCP servers leave option fields unterminated
2144 ; in violation of the spec.
2146 ; For parse_some_dhcp_options, DH contains the minimum value for
2147 ; the option to recognize -- this is used to restrict parsing to
2148 ; PXELINUX-specific options only.
2150 parse_dhcp_options:
2151 xor dx,dx
2153 parse_some_dhcp_options:
2154 .loop:
2155 and cx,cx
2156 jz .done
2158 lodsb
2159 dec cx
2160 jz .done ; Last byte; must be PAD, END or malformed
2161 cmp al, 0 ; PAD option
2162 je .loop
2163 cmp al,255 ; END option
2164 je .done
2166 ; Anything else will have a length field
2167 mov dl,al ; DL <- option number
2168 xor ax,ax
2169 lodsb ; AX <- option length
2170 dec cx
2171 sub cx,ax ; Decrement bytes left counter
2172 jb .done ; Malformed option: length > field size
2174 cmp dl,dh ; Is the option value valid?
2175 jb .opt_done
2177 mov bx,dhcp_option_list
2178 .find_option:
2179 cmp bx,dhcp_option_list_end
2180 jae .opt_done
2181 cmp dl,[bx]
2182 je .found_option
2183 add bx,3
2184 jmp .find_option
2185 .found_option:
2186 pushad
2187 call [bx+1]
2188 popad
2190 ; Fall through
2191 ; Unknown option. Skip to the next one.
2192 .opt_done:
2193 add si,ax
2194 jmp .loop
2195 .done:
2198 section .data
2199 dhcp_option_list:
2200 section .text
2202 %macro dopt 2
2203 section .data
2204 db %1
2205 dw dopt_%2
2206 section .text
2207 dopt_%2:
2208 %endmacro
2211 ; Parse individual DHCP options. SI points to the option data and
2212 ; AX to the option length. DL contains the option number.
2213 ; All registers are saved around the routine.
2215 dopt 1, subnet_mask
2216 mov ebx,[si]
2217 mov [Netmask],ebx
2220 dopt 3, router
2221 mov ebx,[si]
2222 mov [Gateway],ebx
2225 dopt 6, dns_servers
2226 mov cx,ax
2227 shr cx,2
2228 cmp cl,DNS_MAX_SERVERS
2229 jna .oklen
2230 mov cl,DNS_MAX_SERVERS
2231 .oklen:
2232 mov di,DNSServers
2233 rep movsd
2234 mov [LastDNSServer],di
2237 dopt 16, local_domain
2238 mov bx,si
2239 add bx,ax
2240 xor ax,ax
2241 xchg [bx],al ; Zero-terminate option
2242 mov di,LocalDomain
2243 call dns_mangle ; Convert to DNS label set
2244 mov [bx],al ; Restore ending byte
2247 dopt 43, vendor_encaps
2248 mov dh,208 ; Only recognize PXELINUX options
2249 mov cx,ax ; Length of option = max bytes to parse
2250 call parse_some_dhcp_options ; Parse recursive structure
2253 dopt 52, option_overload
2254 mov bl,[si]
2255 mov [OverLoad],bl
2258 dopt 61, client_identifier
2259 cmp ax,MAC_MAX ; Too long?
2260 ja .skip
2261 cmp ax,2 ; Too short?
2262 jb .skip
2263 cmp [MACLen],ah ; Only do this if MACLen == 0
2264 jne .skip
2265 push ax
2266 lodsb ; Client identifier type
2267 cmp al,[MACType]
2268 pop ax
2269 jne .skip ; Client identifier is not a MAC
2270 dec ax
2271 mov [MACLen],al
2272 mov di,MAC
2273 jmp dhcp_copyoption
2274 .skip: ret
2276 dopt 64, bootfile_name
2277 mov di,BootFile
2278 jmp dhcp_copyoption
2280 dopt 97, uuid_client_identifier
2281 cmp ax,17 ; type byte + 16 bytes UUID
2282 jne .skip
2283 mov dl,[si] ; Must have type 0 == UUID
2284 or dl,[HaveUUID] ; Capture only the first instance
2285 jnz .skip
2286 mov byte [HaveUUID],1 ; Got UUID
2287 mov di,UUIDType
2288 jmp dhcp_copyoption
2289 .skip: ret
2291 dopt 208, pxelinux_magic
2292 cmp al,4 ; Must have length == 4
2293 jne .done
2294 cmp dword [si], htonl(0xF100747E) ; Magic number
2295 jne .done
2296 or byte [DHCPMagic],1 ; Found magic #
2297 .done: ret
2299 dopt 209, pxelinux_configfile
2300 mov di,ConfigName
2301 or byte [DHCPMagic],2 ; Got config file
2302 jmp dhcp_copyoption
2304 dopt 210, pxelinux_pathprefix
2305 mov di,PathPrefix
2306 or byte [DHCPMagic],4 ; Got path prefix
2307 jmp dhcp_copyoption
2309 dopt 211, pxelinux_reboottime
2310 cmp al,4
2311 jne .done
2312 mov ebx,[si]
2313 xchg bl,bh ; Convert to host byte order
2314 rol ebx,16
2315 xchg bl,bh
2316 mov [RebootTime],ebx
2317 or byte [DHCPMagic],8 ; Got RebootTime
2318 .done: ret
2320 ; Common code for copying an option verbatim
2321 ; Copies the option into ES:DI and null-terminates it.
2322 ; Returns with AX=0 and SI past the option.
2323 dhcp_copyoption:
2324 xchg cx,ax ; CX <- option length
2325 rep movsb
2326 xchg cx,ax ; AX <- 0
2327 stosb ; Null-terminate
2330 section .data
2331 dhcp_option_list_end:
2332 section .text
2334 section .data
2335 HaveUUID db 0
2336 uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2337 section .text
2340 ; genipopt
2342 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2343 ; option into IPOption based on a DHCP packet in trackbuf.
2344 ; Assumes CS == DS == ES.
2346 genipopt:
2347 pushad
2348 mov di,IPOption
2349 mov eax,'ip='
2350 stosd
2351 dec di
2352 mov eax,[MyIP]
2353 call gendotquad
2354 mov al,':'
2355 stosb
2356 mov eax,[ServerIP]
2357 call gendotquad
2358 mov al,':'
2359 stosb
2360 mov eax,[Gateway]
2361 call gendotquad
2362 mov al,':'
2363 stosb
2364 mov eax,[Netmask]
2365 call gendotquad ; Zero-terminates its output
2366 sub di,IPOption
2367 mov [IPOptionLen],di
2368 popad
2372 ; Call the receive loop while idle. This is done mostly so we can respond to
2373 ; ARP messages, but perhaps in the future this can be used to do network
2374 ; console.
2376 ; hpa sez: people using automatic control on the serial port get very
2377 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2378 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2379 ; passed since the last poll, and reset this when a character is
2380 ; received (RESET_IDLE).
2382 %if HAVE_IDLE
2384 reset_idle:
2385 push ax
2386 mov ax,[cs:BIOS_timer]
2387 mov [cs:IdleTimer],ax
2388 pop ax
2391 check_for_arp:
2392 push ax
2393 mov ax,[cs:BIOS_timer]
2394 sub ax,[cs:IdleTimer]
2395 cmp ax,4
2396 pop ax
2397 jae .need_poll
2399 .need_poll: pushad
2400 push ds
2401 push es
2402 mov ax,cs
2403 mov ds,ax
2404 mov es,ax
2405 mov di,packet_buf
2406 mov [pxe_udp_read_pkt.status],al ; 0
2407 mov [pxe_udp_read_pkt.buffer],di
2408 mov [pxe_udp_read_pkt.buffer+2],ds
2409 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2410 mov eax,[MyIP]
2411 mov [pxe_udp_read_pkt.dip],eax
2412 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2413 mov di,pxe_udp_read_pkt
2414 mov bx,PXENV_UDP_READ
2415 call pxenv
2416 ; Ignore result...
2417 pop es
2418 pop ds
2419 popad
2420 RESET_IDLE
2423 %endif ; HAVE_IDLE
2425 ; -----------------------------------------------------------------------------
2426 ; Common modules
2427 ; -----------------------------------------------------------------------------
2429 %include "getc.inc" ; getc et al
2430 %include "conio.inc" ; Console I/O
2431 %include "writestr.inc" ; String output
2432 writestr equ cwritestr
2433 %include "writehex.inc" ; Hexadecimal output
2434 %include "configinit.inc" ; Initialize configuration
2435 %include "parseconfig.inc" ; High-level config file handling
2436 %include "parsecmd.inc" ; Low-level config file handling
2437 %include "bcopy32.inc" ; 32-bit bcopy
2438 %include "loadhigh.inc" ; Load a file into high memory
2439 %include "font.inc" ; VGA font stuff
2440 %include "graphics.inc" ; VGA graphics
2441 %include "highmem.inc" ; High memory sizing
2442 %include "strcpy.inc" ; strcpy()
2443 %include "rawcon.inc" ; Console I/O w/o using the console functions
2444 %include "dnsresolv.inc" ; DNS resolver
2446 ; -----------------------------------------------------------------------------
2447 ; Begin data section
2448 ; -----------------------------------------------------------------------------
2450 section .data
2452 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2453 db CR, LF, 0
2454 boot_prompt db 'boot: ', 0
2455 wipe_char db BS, ' ', BS, 0
2456 err_notfound db 'Could not find kernel image: ',0
2457 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
2458 err_noram db 'It appears your computer has less than '
2459 asciidec dosram_k
2460 db 'K of low ("DOS")'
2461 db CR, LF
2462 db 'RAM. Linux needs at least this amount to boot. If you get'
2463 db CR, LF
2464 db 'this message in error, hold down the Ctrl key while'
2465 db CR, LF
2466 db 'booting, and I will take your word for it.', CR, LF, 0
2467 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
2468 err_noparm db 'Missing parameter in config file.', CR, LF, 0
2469 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
2470 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
2471 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
2472 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
2473 db CR, LF, 0
2474 err_notdos db ': attempted DOS system call', CR, LF, 0
2475 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
2476 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
2477 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2478 bailmsg equ err_bootfailed
2479 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2480 err_pxefailed db 'PXE API call failed, error ', 0
2481 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2482 err_noconfig db 'Unable to locate configuration file', CR, LF, 0
2483 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2484 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2485 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2486 apiver_str db 'PXE API version is ',0
2487 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2488 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2489 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2490 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2491 undi_data_msg db 'UNDI data segment at: ',0
2492 undi_data_len_msg db 'UNDI data segment size: ',0
2493 undi_code_msg db 'UNDI code segment at: ',0
2494 undi_code_len_msg db 'UNDI code segment size: ',0
2495 cant_free_msg db 'Failed to free base memory, error ', 0
2496 notfound_msg db 'not found', CR, LF, 0
2497 myipaddr_msg db 'My IP address seems to be ',0
2498 tftpprefix_msg db 'TFTP prefix: ', 0
2499 localboot_msg db 'Booting from local disk...', CR, LF, 0
2500 cmdline_msg db 'Command line: ', CR, LF, 0
2501 ready_msg db 'Ready.', CR, LF, 0
2502 trying_msg db 'Trying to load: ', 0
2503 crlfloading_msg db CR, LF ; Fall through
2504 loading_msg db 'Loading ', 0
2505 dotdot_msg db '.'
2506 dot_msg db '.', 0
2507 fourbs_msg db BS, BS, BS, BS, 0
2508 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
2509 crlf_msg db CR, LF
2510 null_msg db 0
2511 crff_msg db CR, FF, 0
2512 default_str db 'default', 0
2513 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2514 cfgprefix db 'pxelinux.cfg/' ; No final null!
2515 cfgprefix_len equ ($-cfgprefix)
2518 ; Command line options we'd like to take a look at
2520 ; mem= and vga= are handled as normal 32-bit integer values
2521 initrd_cmd db 'initrd='
2522 initrd_cmd_len equ $-initrd_cmd
2524 ; This one we make ourselves
2525 bootif_str db 'BOOTIF='
2526 bootif_str_len equ $-bootif_str
2528 ; Config file keyword table
2530 %include "keywords.inc"
2533 ; Extensions to search for (in *forward* order).
2534 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2536 align 4, db 0
2537 exten_table: db '.cbt' ; COMBOOT (specific)
2538 db '.0', 0, 0 ; PXE bootstrap program
2539 db '.com' ; COMBOOT (same as DOS)
2540 db '.c32' ; COM32
2541 exten_table_end:
2542 dd 0, 0 ; Need 8 null bytes here
2545 ; PXE unload sequences
2547 new_api_unload:
2548 db PXENV_UDP_CLOSE
2549 db PXENV_UNDI_SHUTDOWN
2550 db PXENV_UNLOAD_STACK
2551 db PXENV_STOP_UNDI
2552 db 0
2553 old_api_unload:
2554 db PXENV_UDP_CLOSE
2555 db PXENV_UNDI_SHUTDOWN
2556 db PXENV_UNLOAD_STACK
2557 db PXENV_UNDI_CLEANUP
2558 db 0
2561 ; PXE query packets partially filled in
2563 section .bss
2564 pxe_bootp_query_pkt:
2565 .status: resw 1 ; Status
2566 .packettype: resw 1 ; Boot server packet type
2567 .buffersize: resw 1 ; Packet size
2568 .buffer: resw 2 ; seg:off of buffer
2569 .bufferlimit: resw 1 ; Unused
2571 section .data
2572 pxe_udp_open_pkt:
2573 .status: dw 0 ; Status
2574 .sip: dd 0 ; Source (our) IP
2576 pxe_udp_close_pkt:
2577 .status: dw 0 ; Status
2579 pxe_udp_write_pkt:
2580 .status: dw 0 ; Status
2581 .sip: dd 0 ; Server IP
2582 .gip: dd 0 ; Gateway IP
2583 .lport: dw 0 ; Local port
2584 .rport: dw 0 ; Remote port
2585 .buffersize: dw 0 ; Size of packet
2586 .buffer: dw 0, 0 ; seg:off of buffer
2588 pxe_udp_read_pkt:
2589 .status: dw 0 ; Status
2590 .sip: dd 0 ; Source IP
2591 .dip: dd 0 ; Destination (our) IP
2592 .rport: dw 0 ; Remote port
2593 .lport: dw 0 ; Local port
2594 .buffersize: dw 0 ; Max packet size
2595 .buffer: dw 0, 0 ; seg:off of buffer
2598 ; Misc initialized (data) variables
2600 alignb 4, db 0
2601 BaseStack dd StackBuf ; ESP of base stack
2602 dw 0 ; SS of base stack
2603 NextSocket dw 49152 ; Counter for allocating socket numbers
2604 KeepPXE db 0 ; Should PXE be kept around?
2607 ; TFTP commands
2609 tftp_tail db 'octet', 0 ; Octet mode
2610 tsize_str db 'tsize' ,0 ; Request size
2611 tsize_len equ ($-tsize_str)
2612 db '0', 0
2613 blksize_str db 'blksize', 0 ; Request large blocks
2614 blksize_len equ ($-blksize_str)
2615 asciidec TFTP_LARGEBLK
2616 db 0
2617 tftp_tail_len equ ($-tftp_tail)
2619 alignb 2, db 0
2621 ; Options negotiation parsing table (string pointer, string len, offset
2622 ; into socket structure)
2624 tftp_opt_table:
2625 dw tsize_str, tsize_len, tftp_filesize
2626 dw blksize_str, blksize_len, tftp_blksize
2627 tftp_opts equ ($-tftp_opt_table)/6
2630 ; Error packet to return on options negotiation error
2632 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2633 dw TFTP_EOPTNEG ; ERROR 8: bad options
2634 db 'tsize option required', 0 ; Error message
2635 tftp_opt_err_len equ ($-tftp_opt_err)
2637 alignb 4, db 0
2638 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2641 ; IP information (initialized to "unknown" values)
2642 MyIP dd 0 ; My IP address
2643 ServerIP dd 0 ; IP address of boot server
2644 Netmask dd 0 ; Netmask of this subnet
2645 Gateway dd 0 ; Default router
2646 ServerPort dw TFTP_PORT ; TFTP server port
2649 ; Variables that are uninitialized in SYSLINUX but initialized here
2651 alignb 4, db 0
2652 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2653 BufSafeSec dw trackbufsize/512 ; = how many sectors?
2654 BufSafeBytes dw trackbufsize ; = how many bytes?
2655 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
2656 %ifndef DEPEND
2657 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2658 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2659 %endif
2660 %endif