ISOLINUX: support for hybrid mode (CD-ROM/USB key)
[syslinux.git] / core / pxelinux.asm
blob43985828ed10e8bebea1dc264c3727c85f547b8b
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
4 ; pxelinux.asm
6 ; A program to boot Linux kernels off a TFTP server using the Intel PXE
7 ; network booting API. It is based on the SYSLINUX boot loader for
8 ; MS-DOS floppies.
10 ; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
12 ; This program is free software; you can redistribute it and/or modify
13 ; it under the terms of the GNU General Public License as published by
14 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
15 ; Boston MA 02111-1307, USA; either version 2 of the License, or
16 ; (at your option) any later version; incorporated herein by reference.
18 ; ****************************************************************************
20 %define IS_PXELINUX 1
21 %include "head.inc"
22 %include "pxe.inc"
24 ; gPXE extensions support
25 %define GPXE 1
28 ; Some semi-configurable constants... change on your own risk.
30 my_id equ pxelinux_id
31 FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
32 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
33 NULLFILE equ 0 ; Zero byte == null file name
34 NULLOFFSET equ 4 ; Position in which to look
35 REBOOT_TIME equ 5*60 ; If failure, time until full reset
36 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
37 MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
38 MAX_OPEN equ (1 << MAX_OPEN_LG2)
39 PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
40 TFTP_PORT equ htons(69) ; Default TFTP port
41 PKT_RETRY equ 6 ; Packet transmit retry count
42 PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
43 ; Desired TFTP block size
44 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
45 ; be a fair number of networks with "substandard" MTUs which break.
46 ; The code assumes TFTP_LARGEBLK <= 2K.
47 TFTP_MTU equ 1440
48 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
49 ; Standard TFTP block size
50 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
51 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
52 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
54 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
55 SECTOR_SIZE equ TFTP_BLOCKSIZE
57 %define HAVE_IDLE 1 ; idle is not a noop
59 %if HAVE_IDLE
60 %macro RESET_IDLE 0
61 call reset_idle
62 %endmacro
63 %macro DO_IDLE 0
64 call check_for_arp
65 %endmacro
66 %else
67 %macro RESET_IDLE 0
68 ; Nothing
69 %endmacro
70 %macro DO_IDLE 0
71 ; Nothing
72 %endmacro
73 %endif
76 ; TFTP operation codes
78 TFTP_RRQ equ htons(1) ; Read request
79 TFTP_WRQ equ htons(2) ; Write request
80 TFTP_DATA equ htons(3) ; Data packet
81 TFTP_ACK equ htons(4) ; ACK packet
82 TFTP_ERROR equ htons(5) ; ERROR packet
83 TFTP_OACK equ htons(6) ; OACK packet
86 ; TFTP error codes
88 TFTP_EUNDEF equ htons(0) ; Unspecified error
89 TFTP_ENOTFOUND equ htons(1) ; File not found
90 TFTP_EACCESS equ htons(2) ; Access violation
91 TFTP_ENOSPACE equ htons(3) ; Disk full
92 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
93 TFTP_EBADID equ htons(5) ; Unknown transfer
94 TFTP_EEXISTS equ htons(6) ; File exists
95 TFTP_ENOUSER equ htons(7) ; No such user
96 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
99 ; The following structure is used for "virtual kernels"; i.e. LILO-style
100 ; option labels. The options we permit here are `kernel' and `append
101 ; Since there is no room in the bottom 64K for all of these, we
102 ; stick them in high memory and copy them down before we need them.
104 struc vkernel
105 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
106 vk_rname: resb FILENAME_MAX ; Real name
107 vk_ipappend: resb 1 ; "IPAPPEND" flag
108 vk_type: resb 1 ; Type of file
109 vk_appendlen: resw 1
110 alignb 4
111 vk_append: resb max_cmd_len+1 ; Command line
112 alignb 4
113 vk_end: equ $ ; Should be <= vk_size
114 endstruc
117 ; Segment assignments in the bottom 640K
118 ; 0000h - main code/data segment (and BIOS segment)
120 real_mode_seg equ 3000h
121 pktbuf_seg equ 2000h ; Packet buffers segments
122 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
123 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
126 ; BOOTP/DHCP packet pattern
128 struc bootp_t
129 bootp:
130 .opcode resb 1 ; BOOTP/DHCP "opcode"
131 .hardware resb 1 ; ARP hardware type
132 .hardlen resb 1 ; Hardware address length
133 .gatehops resb 1 ; Used by forwarders
134 .ident resd 1 ; Transaction ID
135 .seconds resw 1 ; Seconds elapsed
136 .flags resw 1 ; Broadcast flags
137 .cip resd 1 ; Client IP
138 .yip resd 1 ; "Your" IP
139 .sip resd 1 ; Next server IP
140 .gip resd 1 ; Relay agent IP
141 .macaddr resb 16 ; Client MAC address
142 .sname resb 64 ; Server name (optional)
143 .bootfile resb 128 ; Boot file name
144 .option_magic resd 1 ; Vendor option magic cookie
145 .options resb 1260 ; Vendor options
146 endstruc
148 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
151 ; TFTP connection data structure. Each one of these corresponds to a local
152 ; UDP port. The size of this structure must be a power of 2.
153 ; HBO = host byte order; NBO = network byte order
154 ; (*) = written by options negotiation code, must be dword sized
156 ; For a gPXE connection, we set the local port number to -1 and the
157 ; remote port number contains the gPXE file handle.
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 tftp_goteof resb 1 ; 1 if the EOF packet received
170 resb 3 ; Currently unusued
171 ; At end since it should not be zeroed on socked close
172 tftp_pktbuf resw 1 ; Packet buffer offset
173 endstruc
174 %ifndef DEPEND
175 %if (open_file_t_size & (open_file_t_size-1))
176 %error "open_file_t is not a power of 2"
177 %endif
178 %endif
180 ; ---------------------------------------------------------------------------
181 ; BEGIN CODE
182 ; ---------------------------------------------------------------------------
185 ; Memory below this point is reserved for the BIOS and the MBR
187 section .earlybss
188 trackbufsize equ 8192
189 trackbuf resb trackbufsize ; Track buffer goes here
190 ; ends at 2800h
192 alignb open_file_t_size
193 Files resb MAX_OPEN*open_file_t_size
195 alignb FILENAME_MAX
196 BootFile resb 256 ; Boot file from DHCP packet
197 PathPrefix resb 256 ; Path prefix derived from boot file
198 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
199 IPOption resb 80 ; ip= option buffer
200 InitStack resd 1 ; Pointer to reset stack (SS:SP)
201 PXEStack resd 1 ; Saved stack during PXE call
203 section .bss
204 alignb 4
205 RebootTime resd 1 ; Reboot timeout, if set by option
206 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
207 APIVer resw 1 ; PXE API version found
208 IPOptionLen resw 1 ; Length of IPOption
209 IdleTimer resw 1 ; Time to check for ARP?
210 LocalBootType resw 1 ; Local boot return code
211 PktTimeout resw 1 ; Timeout for current packet
212 RealBaseMem resw 1 ; Amount of DOS memory after freeing
213 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
214 DHCPMagic resb 1 ; PXELINUX magic flags
216 ; The relative position of these fields matter!
217 MAC_MAX equ 32 ; Handle hardware addresses this long
218 MACLen resb 1 ; MAC address len
219 MACType resb 1 ; MAC address type
220 MAC resb MAC_MAX+1 ; Actual MAC address
221 BOOTIFStr resb 7 ; Space for "BOOTIF="
222 MACStr resb 3*(MAC_MAX+1) ; MAC address as a string
224 ; The relative position of these fields matter!
225 UUIDType resb 1 ; Type byte from DHCP option
226 UUID resb 16 ; UUID, from the PXE stack
227 UUIDNull resb 1 ; dhcp_copyoption zero-terminates
230 ; PXE packets which don't need static initialization
232 alignb 4
233 pxe_unload_stack_pkt:
234 .status: resw 1 ; Status
235 .reserved: resw 10 ; Reserved
236 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
238 alignb 16
239 ; BOOTP/DHCP packet buffer
241 section .bss2
242 alignb 16
243 packet_buf resb 2048 ; Transfer packet
244 packet_buf_size equ $-packet_buf
246 section .text
248 ; PXELINUX needs more BSS than the other derivatives;
249 ; therefore we relocate it from 7C00h on startup.
251 StackBuf equ $ ; Base of stack if we use our own
254 ; Primary entry point.
256 bootsec equ $
257 _start:
258 pushfd ; Paranoia... in case of return to PXE
259 pushad ; ... save as much state as possible
260 push ds
261 push es
262 push fs
263 push gs
265 xor ax,ax
266 mov ds,ax
267 mov es,ax
269 jmp 0:_start1 ; Canonicalize address
270 _start1:
271 mov bp,sp
272 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
274 ; That is all pushed onto the PXE stack. Save the pointer
275 ; to it and switch to an internal stack.
276 mov [InitStack],sp
277 mov [InitStack+2],ss
279 %if USE_PXE_PROVIDED_STACK
280 ; Apparently some platforms go bonkers if we
281 ; set up our own stack...
282 mov [BaseStack],sp
283 mov [BaseStack+4],ss
284 %endif
286 cli ; Paranoia
287 lss esp,[BaseStack]
289 sti ; Stack set up and ready
290 cld ; Copy upwards
293 ; Initialize screen (if we're using one)
295 push es ; Save ES -> PXE entry structure
296 push ds
297 pop es ; ES <- DS
298 %include "init.inc"
299 pop es ; Restore ES -> PXE entry structure
301 ; Tell the user we got this far
303 mov si,syslinux_banner
304 call writestr_early
306 mov si,copyright_str
307 call writestr_early
310 ; Assume API version 2.1, in case we find the !PXE structure without
311 ; finding the PXENV+ structure. This should really look at the Base
312 ; Code ROM ID structure in have_pxe, but this is adequate for now --
313 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
314 ; about higher versions than that.
316 mov word [APIVer],0201h
319 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
320 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
321 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
322 ; We should make that the second test, and not trash ES:BX...
324 cmp dword [es:bx], '!PXE'
325 je have_pxe
327 ; Uh-oh, not there... try plan B
328 mov ax, 5650h
329 %if USE_PXE_PROVIDED_STACK == 0
330 lss sp,[InitStack]
331 %endif
332 int 1Ah ; May trash regs
333 %if USE_PXE_PROVIDED_STACK == 0
334 lss esp,[BaseStack]
335 %endif
337 jc no_pxe
338 cmp ax,564Eh
339 jne no_pxe
341 ; Okay, that gave us the PXENV+ structure, find !PXE
342 ; structure from that (if available)
343 cmp dword [es:bx], 'PXEN'
344 jne no_pxe
345 cmp word [es:bx+4], 'V+'
346 je have_pxenv
348 ; Nothing there either. Last-ditch: scan memory
349 call memory_scan_for_pxe_struct ; !PXE scan
350 jnc have_pxe
351 call memory_scan_for_pxenv_struct ; PXENV+ scan
352 jnc have_pxenv
354 no_pxe: mov si,err_nopxe
355 call writestr_early
356 jmp kaboom
358 have_pxenv:
359 mov [StrucPtr],bx
360 mov [StrucPtr+2],es
362 mov si,found_pxenv
363 call writestr_early
365 mov si,apiver_str
366 call writestr_early
367 mov ax,[es:bx+6]
368 mov [APIVer],ax
369 call writehex4
370 call crlf
372 cmp ax,0201h ; API version 2.1 or higher
373 jb old_api
374 mov si,bx
375 mov ax,es
376 les bx,[es:bx+28h] ; !PXE structure pointer
377 cmp dword [es:bx],'!PXE'
378 je have_pxe
380 ; Nope, !PXE structure missing despite API 2.1+, or at least
381 ; the pointer is missing. Do a last-ditch attempt to find it.
382 call memory_scan_for_pxe_struct
383 jnc have_pxe
385 ; Otherwise, no dice, use PXENV+ structure
386 mov bx,si
387 mov es,ax
389 old_api: ; Need to use a PXENV+ structure
390 mov si,using_pxenv_msg
391 call writestr_early
393 mov eax,[es:bx+0Ah] ; PXE RM API
394 mov [PXEEntry],eax
396 mov si,undi_data_msg
397 call writestr_early
398 mov ax,[es:bx+20h]
399 call writehex4
400 call crlf
401 mov si,undi_data_len_msg
402 call writestr_early
403 mov ax,[es:bx+22h]
404 call writehex4
405 call crlf
406 mov si,undi_code_msg
407 call writestr_early
408 mov ax,[es:bx+24h]
409 call writehex4
410 call crlf
411 mov si,undi_code_len_msg
412 call writestr_early
413 mov ax,[es:bx+26h]
414 call writehex4
415 call crlf
417 ; Compute base memory size from PXENV+ structure
418 xor esi,esi
419 movzx eax,word [es:bx+20h] ; UNDI data seg
420 cmp ax,[es:bx+24h] ; UNDI code seg
421 ja .use_data
422 mov ax,[es:bx+24h]
423 mov si,[es:bx+26h]
424 jmp short .combine
425 .use_data:
426 mov si,[es:bx+22h]
427 .combine:
428 shl eax,4
429 add eax,esi
430 shr eax,10 ; Convert to kilobytes
431 mov [RealBaseMem],ax
433 mov si,pxenventry_msg
434 call writestr_early
435 mov ax,[PXEEntry+2]
436 call writehex4
437 mov al,':'
438 call writechr
439 mov ax,[PXEEntry]
440 call writehex4
441 call crlf
442 jmp have_entrypoint
444 have_pxe:
445 mov [StrucPtr],bx
446 mov [StrucPtr+2],es
448 mov eax,[es:bx+10h]
449 mov [PXEEntry],eax
451 mov si,undi_data_msg
452 call writestr_early
453 mov eax,[es:bx+2Ah]
454 call writehex8
455 call crlf
456 mov si,undi_data_len_msg
457 call writestr_early
458 mov ax,[es:bx+2Eh]
459 call writehex4
460 call crlf
461 mov si,undi_code_msg
462 call writestr_early
463 mov ax,[es:bx+32h]
464 call writehex8
465 call crlf
466 mov si,undi_code_len_msg
467 call writestr_early
468 mov ax,[es:bx+36h]
469 call writehex4
470 call crlf
472 ; Compute base memory size from !PXE structure
473 xor esi,esi
474 mov eax,[es:bx+2Ah]
475 cmp eax,[es:bx+32h]
476 ja .use_data
477 mov eax,[es:bx+32h]
478 mov si,[es:bx+36h]
479 jmp short .combine
480 .use_data:
481 mov si,[es:bx+2Eh]
482 .combine:
483 add eax,esi
484 shr eax,10
485 mov [RealBaseMem],ax
487 mov si,pxeentry_msg
488 call writestr_early
489 mov ax,[PXEEntry+2]
490 call writehex4
491 mov al,':'
492 call writechr
493 mov ax,[PXEEntry]
494 call writehex4
495 call crlf
497 have_entrypoint:
498 push cs
499 pop es ; Restore CS == DS == ES
502 ; Network-specific initialization
504 xor ax,ax
505 mov [LocalDomain],al ; No LocalDomain received
508 ; The DHCP client identifiers are best gotten from the DHCPREQUEST
509 ; packet (query info 1).
511 query_bootp_1:
512 mov dl,1
513 call pxe_get_cached_info
514 call parse_dhcp
516 ; We don't use flags from the request packet, so
517 ; this is a good time to initialize DHCPMagic...
518 ; Initialize it to 1 meaning we will accept options found;
519 ; in earlier versions of PXELINUX bit 0 was used to indicate
520 ; we have found option 208 with the appropriate magic number;
521 ; we no longer require that, but MAY want to re-introduce
522 ; it in the future for vendor encapsulated options.
523 mov byte [DHCPMagic],1
526 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
527 ; address). This lives in the DHCPACK packet (query info 2).
529 query_bootp_2:
530 mov dl,2
531 call pxe_get_cached_info
532 call parse_dhcp ; Parse DHCP packet
534 ; Save away MAC address (assume this is in query info 2. If this
535 ; turns out to be problematic it might be better getting it from
536 ; the query info 1 packet.)
538 .save_mac:
539 movzx cx,byte [trackbuf+bootp.hardlen]
540 cmp cx,16
541 jna .mac_ok
542 xor cx,cx ; Bad hardware address length
543 .mac_ok:
544 mov [MACLen],cl
545 mov al,[trackbuf+bootp.hardware]
546 mov [MACType],al
547 mov si,trackbuf+bootp.macaddr
548 mov di,MAC
549 rep movsb
551 ; Enable this if we really need to zero-pad this field...
552 ; mov cx,MAC+MAC_MAX+1
553 ; sub cx,di
554 ; xor ax,ax
555 ; rep stosb
558 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
559 ; packet (query info 3).
561 mov dl,3
562 call pxe_get_cached_info
563 call parse_dhcp ; Parse DHCP packet
566 ; Generate the bootif string, and the hardware-based config string.
568 make_bootif_string:
569 mov si,bootif_str
570 mov di,BOOTIFStr
571 mov cx,bootif_str_len
572 rep movsb
574 movzx cx,byte [MACLen]
575 mov si,MACType
576 inc cx
577 .hexify_mac:
578 push cx
579 mov cl,1 ; CH == 0 already
580 call lchexbytes
581 mov al,'-'
582 stosb
583 pop cx
584 loop .hexify_mac
585 mov [di-1],cl ; Null-terminate and strip final dash
587 ; Generate ip= option
589 call genipopt
592 ; Print IP address
594 mov eax,[MyIP]
595 mov di,DotQuadBuf
596 push di
597 call gendotquad ; This takes network byte order input
599 xchg ah,al ; Convert to host byte order
600 ror eax,16 ; (BSWAP doesn't work on 386)
601 xchg ah,al
603 mov si,myipaddr_msg
604 call writestr_early
605 call writehex8
606 mov al,' '
607 call writechr
608 pop si ; DotQuadBuf
609 call writestr_early
610 call crlf
612 mov si,IPOption
613 call writestr_early
614 call crlf
617 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
618 ; if we didn't get the magic enable, do not recognize any other options.
620 check_dhcp_magic:
621 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
622 jnz .got_magic
623 mov byte [DHCPMagic], 0 ; If not, kill all other options
624 .got_magic:
628 ; Initialize UDP stack
630 udp_init:
631 mov eax,[MyIP]
632 mov [pxe_udp_open_pkt.sip],eax
633 mov di,pxe_udp_open_pkt
634 mov bx,PXENV_UDP_OPEN
635 call pxenv
636 jc .failed
637 cmp word [pxe_udp_open_pkt.status], byte 0
638 je .success
639 .failed: mov si,err_udpinit
640 call writestr_early
641 jmp kaboom
642 .success:
645 ; Common initialization code
647 %include "cpuinit.inc"
650 ; Detect NIC type and initialize the idle mechanism
652 call pxe_detect_nic_type
653 RESET_IDLE
656 ; Now we're all set to start with our *real* business. First load the
657 ; configuration file (if any) and parse it.
659 ; In previous versions I avoided using 32-bit registers because of a
660 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
661 ; random. I figure, though, that if there are any of those still left
662 ; they probably won't be trying to install Linux on them...
664 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
665 ; to take'm out. In fact, we may want to put them back if we're going
666 ; to boot ELKS at some point.
670 ; Store standard filename prefix
672 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
673 jnz .got_prefix
674 mov si,BootFile
675 mov di,PathPrefix
677 call strcpy
678 mov cx,di
679 sub cx,PathPrefix+1
681 lea si,[di-2] ; Skip final null!
682 .find_alnum: lodsb
683 or al,20h
684 cmp al,'.' ; Count . or - as alphanum
685 je .alnum
686 cmp al,'-'
687 je .alnum
688 cmp al,'0'
689 jb .notalnum
690 cmp al,'9'
691 jbe .alnum
692 cmp al,'a'
693 jb .notalnum
694 cmp al,'z'
695 ja .notalnum
696 .alnum: loop .find_alnum
697 dec si
698 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
700 .got_prefix:
701 mov si,tftpprefix_msg
702 call writestr_early
703 mov si,PathPrefix
704 call writestr_early
705 call crlf
708 ; Load configuration file
710 find_config:
713 ; Begin looking for configuration file
715 config_scan:
716 test byte [DHCPMagic], 02h
717 jz .no_option
719 ; We got a DHCP option, try it first
720 call .try
721 jnz .success
723 .no_option:
724 mov di,ConfigName
725 mov si,cfgprefix
726 mov cx,cfgprefix_len
727 rep movsb
729 ; Have to guess config file name...
731 ; Try loading by UUID.
732 cmp byte [HaveUUID],0
733 je .no_uuid
735 push di
736 mov bx,uuid_dashes
737 mov si,UUID
738 .gen_uuid:
739 movzx cx,byte [bx]
740 jcxz .done_uuid
741 inc bx
742 call lchexbytes
743 mov al,'-'
744 stosb
745 jmp .gen_uuid
746 .done_uuid:
747 mov [di-1],cl ; Remove last dash and zero-terminate
748 pop di
749 call .try
750 jnz .success
751 .no_uuid:
753 ; Try loading by MAC address
754 push di
755 mov si,MACStr
756 call strcpy
757 pop di
758 call .try
759 jnz .success
761 ; Nope, try hexadecimal IP prefixes...
762 .scan_ip:
763 mov cx,4
764 mov si,MyIP
765 call uchexbytes ; Convert to hex string
767 mov cx,8 ; Up to 8 attempts
768 .tryagain:
769 mov byte [di],0 ; Zero-terminate string
770 call .try
771 jnz .success
772 dec di ; Drop one character
773 loop .tryagain
775 ; Final attempt: "default" string
776 mov si,default_str ; "default" string
777 call strcpy
778 call .try
779 jnz .success
781 mov si,err_noconfig
782 call writestr_early
783 jmp kaboom
785 .try:
786 pusha
787 mov si,trying_msg
788 call writestr_early
789 mov di,ConfigName
790 mov si,di
791 call writestr_early
792 call crlf
793 mov si,di
794 mov di,KernelName ; Borrow this buffer for mangled name
795 call mangle_name
796 call open
797 popa
801 .success:
804 ; Linux kernel loading code is common. However, we need to define
805 ; a couple of helper macros...
808 ; Handle "ipappend" option
809 %define HAVE_SPECIAL_APPEND
810 %macro SPECIAL_APPEND 0
811 test byte [IPAppend],01h ; ip=
812 jz .noipappend1
813 mov si,IPOption
814 mov cx,[IPOptionLen]
815 rep movsb
816 mov al,' '
817 stosb
818 .noipappend1:
819 test byte [IPAppend],02h
820 jz .noipappend2
821 mov si,BOOTIFStr
822 call strcpy
823 mov byte [es:di-1],' ' ; Replace null with space
824 .noipappend2:
825 %endmacro
827 ; Unload PXE stack
828 %define HAVE_UNLOAD_PREP
829 %macro UNLOAD_PREP 0
830 call unload_pxe
831 %endmacro
834 ; Now we have the config file open. Parse the config file and
835 ; run the user interface.
837 %include "ui.inc"
840 ; Boot to the local disk by returning the appropriate PXE magic.
841 ; AX contains the appropriate return code.
843 %if HAS_LOCALBOOT
845 local_boot:
846 push cs
847 pop ds
848 mov [LocalBootType],ax
849 call vgaclearmode
850 mov si,localboot_msg
851 call writestr_early
852 ; Restore the environment we were called with
853 lss sp,[InitStack]
854 pop gs
855 pop fs
856 pop es
857 pop ds
858 popad
859 mov ax,[cs:LocalBootType]
860 popfd
861 retf ; Return to PXE
863 %endif
866 ; kaboom: write a message and bail out. Wait for quite a while,
867 ; or a user keypress, then do a hard reboot.
869 kaboom:
870 RESET_STACK_AND_SEGS AX
871 .patch: mov si,bailmsg
872 call writestr_early ; Returns with AL = 0
873 .drain: call pollchar
874 jz .drained
875 call getchar
876 jmp short .drain
877 .drained:
878 mov edi,[RebootTime]
879 mov al,[DHCPMagic]
880 and al,09h ; Magic+Timeout
881 cmp al,09h
882 je .time_set
883 mov edi,REBOOT_TIME
884 .time_set:
885 mov cx,18
886 .wait1: push cx
887 mov ecx,edi
888 .wait2: mov dx,[BIOS_timer]
889 .wait3: call pollchar
890 jnz .keypress
891 cmp dx,[BIOS_timer]
892 je .wait3
893 loop .wait2,ecx
894 mov al,'.'
895 call writechr
896 pop cx
897 loop .wait1
898 .keypress:
899 call crlf
900 mov word [BIOS_magic],0 ; Cold reboot
901 jmp 0F000h:0FFF0h ; Reset vector address
904 ; memory_scan_for_pxe_struct:
906 ; If none of the standard methods find the !PXE structure, look for it
907 ; by scanning memory.
909 ; On exit, if found:
910 ; CF = 0, ES:BX -> !PXE structure
911 ; Otherwise CF = 1, all registers saved
913 memory_scan_for_pxe_struct:
914 push ds
915 pusha
916 mov ax,cs
917 mov ds,ax
918 mov si,trymempxe_msg
919 call writestr_early
920 mov ax,[BIOS_fbm] ; Starting segment
921 shl ax,(10-4) ; Kilobytes -> paragraphs
922 ; mov ax,01000h ; Start to look here
923 dec ax ; To skip inc ax
924 .mismatch:
925 inc ax
926 cmp ax,0A000h ; End of memory
927 jae .not_found
928 call writehex4
929 mov si,fourbs_msg
930 call writestr_early
931 mov es,ax
932 mov edx,[es:0]
933 cmp edx,'!PXE'
934 jne .mismatch
935 movzx cx,byte [es:4] ; Length of structure
936 cmp cl,08h ; Minimum length
937 jb .mismatch
938 push ax
939 xor ax,ax
940 xor si,si
941 .checksum: es lodsb
942 add ah,al
943 loop .checksum
944 pop ax
945 jnz .mismatch ; Checksum must == 0
946 .found: mov bp,sp
947 xor bx,bx
948 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
949 mov ax,es
950 call writehex4
951 call crlf
952 popa
953 pop ds
956 .not_found: mov si,notfound_msg
957 call writestr_early
958 popa
959 pop ds
964 ; memory_scan_for_pxenv_struct:
966 ; If none of the standard methods find the PXENV+ structure, look for it
967 ; by scanning memory.
969 ; On exit, if found:
970 ; CF = 0, ES:BX -> PXENV+ structure
971 ; Otherwise CF = 1, all registers saved
973 memory_scan_for_pxenv_struct:
974 pusha
975 mov si,trymempxenv_msg
976 call writestr_early
977 ; mov ax,[BIOS_fbm] ; Starting segment
978 ; shl ax,(10-4) ; Kilobytes -> paragraphs
979 mov ax,01000h ; Start to look here
980 dec ax ; To skip inc ax
981 .mismatch:
982 inc ax
983 cmp ax,0A000h ; End of memory
984 jae .not_found
985 mov es,ax
986 mov edx,[es:0]
987 cmp edx,'PXEN'
988 jne .mismatch
989 mov dx,[es:4]
990 cmp dx,'V+'
991 jne .mismatch
992 movzx cx,byte [es:8] ; Length of structure
993 cmp cl,26h ; Minimum length
994 jb .mismatch
995 xor ax,ax
996 xor si,si
997 .checksum: es lodsb
998 add ah,al
999 loop .checksum
1000 and ah,ah
1001 jnz .mismatch ; Checksum must == 0
1002 .found: mov bp,sp
1003 mov [bp+8],bx ; Save BX into stack frame
1004 mov ax,bx
1005 call writehex4
1006 call crlf
1009 .not_found: mov si,notfound_msg
1010 call writestr_early
1011 popad
1016 ; close_file:
1017 ; Deallocates a file structure (pointer in SI)
1018 ; Assumes CS == DS.
1020 ; XXX: We should check to see if this file is still open on the server
1021 ; side and send a courtesy ERROR packet to the server.
1023 close_file:
1024 and si,si
1025 jz .closed
1026 mov word [si],0 ; Not in use
1027 .closed: ret
1030 ; searchdir:
1032 ; Open a TFTP connection to the server
1034 ; On entry:
1035 ; DS:DI = mangled filename
1036 ; If successful:
1037 ; ZF clear
1038 ; SI = socket pointer
1039 ; EAX = file length in bytes, or -1 if unknown
1040 ; If unsuccessful
1041 ; ZF set
1044 searchdir:
1045 push es
1046 push bx
1047 push cx
1048 mov ax,ds
1049 mov es,ax
1050 mov si,di
1051 push bp
1052 mov bp,sp
1054 call allocate_socket
1055 jz .ret
1057 mov ax,PKT_RETRY ; Retry counter
1058 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1060 .sendreq: push ax ; [bp-2] - Retry counter
1061 push si ; [bp-4] - File name
1063 mov di,packet_buf
1064 mov [pxe_udp_write_pkt.buffer],di
1066 mov ax,TFTP_RRQ ; TFTP opcode
1067 stosw
1069 lodsd ; EAX <- server override (if any)
1070 and eax,eax
1071 jnz .noprefix ; No prefix, and we have the server
1073 push si ; Add common prefix
1074 mov si,PathPrefix
1075 call strcpy
1076 dec di
1077 pop si
1079 mov eax,[ServerIP] ; Get default server
1081 .noprefix:
1082 call strcpy ; Filename
1083 %if GPXE
1084 mov si,packet_buf+2
1085 call is_gpxe
1086 jnc .gpxe
1087 %endif
1089 mov [bx+tftp_remoteip],eax
1091 push bx ; [bp-6] - TFTP block
1092 mov bx,[bx]
1093 push bx ; [bp-8] - TID (local port no)
1095 mov [pxe_udp_write_pkt.sip],eax
1096 ; Now figure out the gateway
1097 xor eax,[MyIP]
1098 and eax,[Netmask]
1099 jz .nogwneeded
1100 mov eax,[Gateway]
1101 .nogwneeded:
1102 mov [pxe_udp_write_pkt.gip],eax
1103 mov [pxe_udp_write_pkt.lport],bx
1104 mov ax,[ServerPort]
1105 mov [pxe_udp_write_pkt.rport],ax
1106 mov si,tftp_tail
1107 mov cx,tftp_tail_len
1108 rep movsb
1109 sub di,packet_buf ; Get packet size
1110 mov [pxe_udp_write_pkt.buffersize],di
1112 mov di,pxe_udp_write_pkt
1113 mov bx,PXENV_UDP_WRITE
1114 call pxenv
1115 jc .failure
1116 cmp word [pxe_udp_write_pkt.status],byte 0
1117 jne .failure
1120 ; Danger, Will Robinson! We need to support timeout
1121 ; and retry lest we just lost a packet...
1124 ; Packet transmitted OK, now we need to receive
1125 .getpacket: push word [PktTimeout] ; [bp-10]
1126 push word [BIOS_timer] ; [bp-12]
1128 .pkt_loop: mov bx,[bp-8] ; TID
1129 mov di,packet_buf
1130 mov [pxe_udp_read_pkt.buffer],di
1131 mov [pxe_udp_read_pkt.buffer+2],ds
1132 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1133 mov eax,[MyIP]
1134 mov [pxe_udp_read_pkt.dip],eax
1135 mov [pxe_udp_read_pkt.lport],bx
1136 mov di,pxe_udp_read_pkt
1137 mov bx,PXENV_UDP_READ
1138 call pxenv
1139 jnc .got_packet ; Wait for packet
1140 .no_packet:
1141 mov dx,[BIOS_timer]
1142 cmp dx,[bp-12]
1143 je .pkt_loop
1144 mov [bp-12],dx
1145 dec word [bp-10] ; Timeout
1146 jnz .pkt_loop
1147 pop ax ; Adjust stack
1148 pop ax
1149 shl word [PktTimeout],1 ; Exponential backoff
1150 jmp .failure
1152 .got_packet:
1153 mov si,[bp-6] ; TFTP pointer
1154 mov bx,[bp-8] ; TID
1156 ; Make sure the packet actually came from the server
1157 ; This is technically not to the TFTP spec?
1158 mov eax,[si+tftp_remoteip]
1159 cmp [pxe_udp_read_pkt.sip],eax
1160 jne .no_packet
1162 ; Got packet - reset timeout
1163 mov word [PktTimeout],PKT_TIMEOUT
1165 pop ax ; Adjust stack
1166 pop ax
1168 mov ax,[pxe_udp_read_pkt.rport]
1169 mov [si+tftp_remoteport],ax
1171 ; filesize <- -1 == unknown
1172 mov dword [si+tftp_filesize], -1
1173 ; Default blksize unless blksize option negotiated
1174 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1176 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1177 sub cx,2 ; CX <- bytes after opcode
1178 jb .failure ; Garbled reply
1180 mov si,packet_buf
1181 lodsw
1183 cmp ax, TFTP_ERROR
1184 je .bailnow ; ERROR reply: don't try again
1186 ; If the server doesn't support any options, we'll get
1187 ; a DATA reply instead of OACK. Stash the data in
1188 ; the file buffer and go with the default value for
1189 ; all options...
1190 cmp ax, TFTP_DATA
1191 je .no_oack
1193 cmp ax, TFTP_OACK
1194 jne .err_reply ; Unknown packet type
1196 ; Now we need to parse the OACK packet to get the transfer
1197 ; and packet sizes.
1198 ; SI -> first byte of options; [E]CX -> byte count
1199 .parse_oack:
1200 jcxz .done_pkt ; No options acked
1201 .get_opt_name:
1202 mov di,si
1203 mov bx,si
1204 .opt_name_loop: lodsb
1205 and al,al
1206 jz .got_opt_name
1207 or al,20h ; Convert to lowercase
1208 stosb
1209 loop .opt_name_loop
1210 ; We ran out, and no final null
1211 jmp .err_reply
1212 .got_opt_name: ; si -> option value
1213 dec cx ; bytes left in pkt
1214 jz .err_reply ; Option w/o value
1216 ; Parse option pointed to by bx; guaranteed to be
1217 ; null-terminated.
1218 push cx
1219 push si
1220 mov si,bx ; -> option name
1221 mov bx,tftp_opt_table
1222 mov cx,tftp_opts
1223 .opt_loop:
1224 push cx
1225 push si
1226 mov di,[bx] ; Option pointer
1227 mov cx,[bx+2] ; Option len
1228 repe cmpsb
1229 pop si
1230 pop cx
1231 je .get_value ; OK, known option
1232 add bx,6
1233 loop .opt_loop
1235 pop si
1236 pop cx
1237 jmp .err_reply ; Non-negotiated option returned
1239 .get_value: pop si ; si -> option value
1240 pop cx ; cx -> bytes left in pkt
1241 mov bx,[bx+4] ; Pointer to data target
1242 add bx,[bp-6] ; TFTP socket pointer
1243 xor eax,eax
1244 xor edx,edx
1245 .value_loop: lodsb
1246 and al,al
1247 jz .got_value
1248 sub al,'0'
1249 cmp al, 9
1250 ja .err_reply ; Not a decimal digit
1251 imul edx,10
1252 add edx,eax
1253 mov [bx],edx
1254 loop .value_loop
1255 ; Ran out before final null, accept anyway
1256 jmp short .done_pkt
1258 .got_value:
1259 dec cx
1260 jnz .get_opt_name ; Not end of packet
1262 ; ZF == 1
1264 ; Success, done!
1265 .done_pkt:
1266 pop si ; Junk
1267 pop si ; We want the packet ptr in SI
1269 mov eax,[si+tftp_filesize]
1270 .got_file: ; SI->socket structure, EAX = size
1271 and eax,eax ; Set ZF depending on file size
1272 jz .error_si ; ZF = 1 need to free the socket
1273 .ret:
1274 leave ; SP <- BP, POP BP
1275 pop cx
1276 pop bx
1277 pop es
1281 .no_oack: ; We got a DATA packet, meaning no options are
1282 ; suported. Save the data away and consider the length
1283 ; undefined, *unless* this is the only data packet...
1284 mov bx,[bp-6] ; File pointer
1285 sub cx,2 ; Too short?
1286 jb .failure
1287 lodsw ; Block number
1288 cmp ax,htons(1)
1289 jne .failure
1290 mov [bx+tftp_lastpkt],ax
1291 cmp cx,TFTP_BLOCKSIZE
1292 ja .err_reply ; Corrupt...
1293 je .not_eof
1294 ; This was the final EOF packet, already...
1295 ; We know the filesize, but we also want to ack the
1296 ; packet and set the EOF flag.
1297 mov [bx+tftp_filesize],ecx
1298 mov byte [bx+tftp_goteof],1
1299 push si
1300 mov si,bx
1301 ; AX = htons(1) already
1302 call ack_packet
1303 pop si
1304 .not_eof:
1305 mov [bx+tftp_bytesleft],cx
1306 mov ax,pktbuf_seg
1307 push es
1308 mov es,ax
1309 mov di,tftp_pktbuf
1310 mov [bx+tftp_dataptr],di
1311 add cx,3
1312 shr cx,2
1313 rep movsd
1314 pop es
1315 jmp .done_pkt
1317 .err_reply: ; Option negotiation error. Send ERROR reply.
1318 ; ServerIP and gateway are already programmed in
1319 mov si,[bp-6]
1320 mov ax,[si+tftp_remoteport]
1321 mov word [pxe_udp_write_pkt.rport],ax
1322 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1323 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1324 mov di,pxe_udp_write_pkt
1325 mov bx,PXENV_UDP_WRITE
1326 call pxenv
1328 ; Write an error message and explode
1329 mov si,err_damage
1330 call writestr_early
1331 jmp kaboom
1333 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1335 .failure: pop bx ; Junk
1336 pop bx
1337 pop si
1338 pop ax
1339 dec ax ; Retry counter
1340 jnz .sendreq ; Try again
1342 .error: mov si,bx ; Socket pointer
1343 .error_si: ; Socket pointer already in SI
1344 call free_socket ; ZF <- 1, SI <- 0
1345 jmp .ret
1348 %if GPXE
1349 .gpxe:
1350 push bx ; Socket pointer
1351 mov di,gpxe_file_open
1352 mov word [di],2 ; PXENV_STATUS_BAD_FUNC
1353 mov word [di+4],packet_buf+2 ; Completed URL
1354 mov [di+6],ds
1355 mov bx,PXENV_FILE_OPEN
1356 call pxenv
1357 pop si ; Socket pointer in SI
1358 jc .error_si
1360 mov ax,[di+2]
1361 mov word [si+tftp_localport],-1 ; gPXE URL
1362 mov [si+tftp_remoteport],ax
1363 mov di,gpxe_get_file_size
1364 mov [di+2],ax
1366 %if 0
1367 ; Disable this for now since gPXE doesn't always
1368 ; return valid information in PXENV_GET_FILE_SIZE
1369 mov bx,PXENV_GET_FILE_SIZE
1370 call pxenv
1371 mov eax,[di+4] ; File size
1372 jnc .oksize
1373 %endif
1374 or eax,-1 ; Size unknown
1375 .oksize:
1376 mov [si+tftp_filesize],eax
1377 jmp .got_file
1378 %endif ; GPXE
1381 ; allocate_socket: Allocate a local UDP port structure
1383 ; If successful:
1384 ; ZF set
1385 ; BX = socket pointer
1386 ; If unsuccessful:
1387 ; ZF clear
1389 allocate_socket:
1390 push cx
1391 mov bx,Files
1392 mov cx,MAX_OPEN
1393 .check: cmp word [bx], byte 0
1394 je .found
1395 add bx,open_file_t_size
1396 loop .check
1397 xor cx,cx ; ZF = 1
1398 pop cx
1400 ; Allocate a socket number. Socket numbers are made
1401 ; guaranteed unique by including the socket slot number
1402 ; (inverted, because we use the loop counter cx); add a
1403 ; counter value to keep the numbers from being likely to
1404 ; get immediately reused.
1406 ; The NextSocket variable also contains the top two bits
1407 ; set. This generates a value in the range 49152 to
1408 ; 57343.
1409 .found:
1410 dec cx
1411 push ax
1412 mov ax,[NextSocket]
1413 inc ax
1414 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1415 mov [NextSocket],ax
1416 shl cx,13-MAX_OPEN_LG2
1417 add cx,ax ; ZF = 0
1418 xchg ch,cl ; Convert to network byte order
1419 mov [bx],cx ; Socket in use
1420 pop ax
1421 pop cx
1425 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1427 free_socket:
1428 push es
1429 pusha
1430 xor ax,ax
1431 mov es,ax
1432 mov di,si
1433 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1434 rep stosw
1435 popa
1436 pop es
1437 xor si,si
1441 ; parse_dotquad:
1442 ; Read a dot-quad pathname in DS:SI and output an IP
1443 ; address in EAX, with SI pointing to the first
1444 ; nonmatching character.
1446 ; Return CF=1 on error.
1448 ; No segment assumptions permitted.
1450 parse_dotquad:
1451 push cx
1452 mov cx,4
1453 xor eax,eax
1454 .parseloop:
1455 mov ch,ah
1456 mov ah,al
1457 lodsb
1458 sub al,'0'
1459 jb .notnumeric
1460 cmp al,9
1461 ja .notnumeric
1462 aad ; AL += 10 * AH; AH = 0;
1463 xchg ah,ch
1464 jmp .parseloop
1465 .notnumeric:
1466 cmp al,'.'-'0'
1467 pushf
1468 mov al,ah
1469 mov ah,ch
1470 xor ch,ch
1471 ror eax,8
1472 popf
1473 jne .error
1474 loop .parseloop
1475 jmp .done
1476 .error:
1477 loop .realerror ; If CX := 1 then we're done
1479 jmp .done
1480 .realerror:
1482 .done:
1483 dec si ; CF unchanged!
1484 pop cx
1488 ; is_url: Return CF=0 if and only if the buffer pointed to by
1489 ; DS:SI is a URL (contains ://). No registers modified.
1491 %if GPXE
1492 is_url:
1493 push si
1494 push eax
1495 .loop:
1496 mov eax,[si]
1497 inc si
1498 and al,al
1499 jz .not_url
1500 and eax,0FFFFFFh
1501 cmp eax,'://'
1502 jne .loop
1503 .done:
1504 ; CF=0 here
1505 pop eax
1506 pop si
1508 .not_url:
1510 jmp .done
1513 ; is_gpxe: Return CF=0 if and only if the buffer pointed to by
1514 ; DS:SI is a URL (contains ://) *and* the gPXE extensions
1515 ; API is available. No registers modified.
1517 is_gpxe:
1518 call is_url
1519 jc .ret ; Not a URL, don't bother
1520 .again:
1521 cmp byte [HasGPXE],1
1522 ja .unknown
1523 ; CF=1 if not available (0),
1524 ; CF=0 if known available (1).
1525 .ret: ret
1527 .unknown:
1528 ; If we get here, the gPXE status is unknown.
1529 push es
1530 pushad
1531 push ds
1532 pop es
1533 mov di,gpxe_file_api_check
1534 mov bx,PXENV_FILE_API_CHECK ; BH = 0
1535 call pxenv
1536 jc .nogood
1537 cmp dword [di+4],0xe9c17b20
1538 jne .nogood
1539 mov ax,[di+12] ; Don't care about the upper half...
1540 not ax ; Set bits of *missing* functions...
1541 and ax,01001011b ; The functions we care about
1542 setz bh
1543 jz .done
1544 .nogood:
1545 mov si,gpxe_warning_msg
1546 call writestr_early
1547 .done:
1548 mov [HasGPXE],bh
1549 popad
1550 pop es
1551 jmp .again
1553 section .data
1554 gpxe_warning_msg:
1555 db 'URL syntax, but gPXE extensions not detected, '
1556 db 'trying plain TFTP...', CR, LF, 0
1557 HasGPXE db -1 ; Unknown
1558 section .text
1560 %endif
1563 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1564 ; to by ES:DI; ends on encountering any whitespace.
1565 ; DI is preserved.
1567 ; This verifies that a filename is < FILENAME_MAX characters
1568 ; and doesn't contain whitespace, and zero-pads the output buffer,
1569 ; so "repe cmpsb" can do a compare.
1571 ; The first four bytes of the manged name is the IP address of
1572 ; the download host, 0 for no host, or -1 for a gPXE URL.
1574 ; No segment assumptions permitted.
1576 mangle_name:
1577 push di
1578 %if GPXE
1579 call is_url
1580 jc .not_url
1581 or eax,-1 ; It's a URL
1582 jmp .prefix_done
1583 .not_url:
1584 %endif ; GPXE
1585 push si
1586 mov eax,[cs:ServerIP]
1587 cmp byte [si],0
1588 je .noip ; Null filename?!?!
1589 cmp word [si],'::' ; Leading ::?
1590 je .gotprefix
1592 .more:
1593 inc si
1594 cmp byte [si],0
1595 je .noip
1596 cmp word [si],'::'
1597 jne .more
1599 ; We have a :: prefix of some sort, it could be either
1600 ; a DNS name or a dot-quad IP address. Try the dot-quad
1601 ; first...
1602 .here:
1603 pop si
1604 push si
1605 call parse_dotquad
1606 jc .notdq
1607 cmp word [si],'::'
1608 je .gotprefix
1609 .notdq:
1610 pop si
1611 push si
1612 call dns_resolv
1613 cmp word [si],'::'
1614 jne .noip
1615 and eax,eax
1616 jnz .gotprefix
1618 .noip:
1619 pop si
1620 xor eax,eax
1621 jmp .prefix_done
1623 .gotprefix:
1624 pop cx ; Adjust stack
1625 inc si ; Skip double colon
1626 inc si
1628 .prefix_done:
1629 stosd ; Save IP address prefix
1630 mov cx,FILENAME_MAX-5
1632 .mn_loop:
1633 lodsb
1634 cmp al,' ' ; If control or space, end
1635 jna .mn_end
1636 stosb
1637 loop .mn_loop
1638 .mn_end:
1639 inc cx ; At least one null byte
1640 xor ax,ax ; Zero-fill name
1641 rep stosb ; Doesn't do anything if CX=0
1642 pop di
1643 ret ; Done
1646 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1647 ; filename to the conventional representation. This is needed
1648 ; for the BOOT_IMAGE= parameter for the kernel.
1650 ; NOTE: The output buffer needs to be able to hold an
1651 ; expanded IP address.
1653 ; DS:SI -> input mangled file name
1654 ; ES:DI -> output buffer
1656 ; On return, DI points to the first byte after the output name,
1657 ; which is set to a null byte.
1659 unmangle_name:
1660 push eax
1661 lodsd
1662 and eax,eax
1663 jz .noip
1664 cmp eax,-1
1665 jz .noip ; URL
1666 call gendotquad
1667 mov ax,'::'
1668 stosw
1669 .noip:
1670 call strcpy
1671 dec di ; Point to final null byte
1672 pop eax
1676 ; pxenv
1678 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1679 ; calling convention. This is a separate local routine so
1680 ; we can hook special things from it if necessary. In particular,
1681 ; some PXE stacks seem to not like being invoked from anything but
1682 ; the initial stack, so humour it.
1684 ; While we're at it, save and restore all registers.
1686 pxenv:
1687 pushad
1688 %if USE_PXE_PROVIDED_STACK == 0
1689 mov [cs:PXEStack],sp
1690 mov [cs:PXEStack+2],ss
1691 lss sp,[cs:InitStack]
1692 %endif
1693 ; Pre-clear the Status field
1694 mov word [es:di],cs
1696 ; This works either for the PXENV+ or the !PXE calling
1697 ; convention, as long as we ignore CF (which is redundant
1698 ; with AX anyway.)
1699 push es
1700 push di
1701 push bx
1702 .jump: call 0:0
1703 add sp,6
1704 mov [cs:PXEStatus],ax
1705 add ax,-1 ; Set CF unless AX was 0
1707 %if USE_PXE_PROVIDED_STACK == 0
1708 lss sp,[cs:PXEStack]
1709 %endif
1711 ; This clobbers the AX return, but we don't use it
1712 ; except for testing it against zero (and setting CF),
1713 ; which we did above. For anything else,
1714 ; use the Status field in the reply.
1715 ; For the COMBOOT function, the value is saved in
1716 ; the PXEStatus variable.
1717 popad
1718 cld ; Make sure DF <- 0
1721 ; Must be after function def due to NASM bug
1722 PXEEntry equ pxenv.jump+1
1724 section .bss
1725 alignb 2
1726 PXEStatus resb 2
1728 section .text
1731 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1733 ; In this case, get multiple blocks from a specific TCP connection.
1735 ; On entry:
1736 ; ES:BX -> Buffer
1737 ; SI -> TFTP socket pointer
1738 ; CX -> 512-byte block count; 0FFFFh = until end of file
1739 ; On exit:
1740 ; SI -> TFTP socket pointer (or 0 on EOF)
1741 ; CF = 1 -> Hit EOF
1742 ; ECX -> number of bytes actually read
1744 getfssec:
1745 push eax
1746 push edi
1747 push bx
1748 push si
1749 push fs
1750 mov di,bx
1751 mov ax,pktbuf_seg
1752 mov fs,ax
1754 xor eax,eax
1755 movzx ecx,cx
1756 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1757 push ecx ; Initial request size
1758 jz .hit_eof ; Nothing to do?
1760 .need_more:
1761 call fill_buffer
1762 movzx eax,word [si+tftp_bytesleft]
1763 and ax,ax
1764 jz .hit_eof
1766 push ecx
1767 cmp ecx,eax
1768 jna .ok_size
1769 mov ecx,eax
1770 .ok_size:
1771 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1772 mov bx,[si+tftp_dataptr]
1773 sub [si+tftp_bytesleft],cx
1774 xchg si,bx
1775 fs rep movsb ; Copy from packet buffer
1776 xchg si,bx
1777 mov [si+tftp_dataptr],bx
1779 pop ecx
1780 sub ecx,eax
1781 jnz .need_more
1783 .hit_eof:
1784 call fill_buffer
1786 pop eax ; Initial request amount
1787 xchg eax,ecx
1788 sub ecx,eax ; ... minus anything not gotten
1790 pop fs
1791 pop si
1793 ; Is there anything left of this?
1794 mov eax,[si+tftp_filesize]
1795 sub eax,[si+tftp_filepos]
1796 jnz .bytes_left
1798 cmp [si+tftp_bytesleft],ax ; AX == 0
1799 jne .bytes_left
1801 cmp byte [si+tftp_goteof],0
1802 je .done
1803 ; I'm 99% sure this can't happen, but...
1804 call fill_buffer ; Receive/ACK the EOF packet
1805 .done:
1806 ; The socket is closed and the buffer drained
1807 ; Close socket structure and re-init for next user
1808 call free_socket
1810 jmp .ret
1811 .bytes_left:
1813 .ret:
1814 pop bx
1815 pop edi
1816 pop eax
1820 ; Get a fresh packet if the buffer is drained, and we haven't hit
1821 ; EOF yet. The buffer should be filled immediately after draining!
1823 ; expects fs -> pktbuf_seg and ds:si -> socket structure
1825 fill_buffer:
1826 cmp word [si+tftp_bytesleft],0
1827 je .empty
1828 ret ; Otherwise, nothing to do
1830 .empty:
1831 push es
1832 pushad
1833 mov ax,ds
1834 mov es,ax
1836 ; Note: getting the EOF packet is not the same thing
1837 ; as tftp_filepos == tftp_filesize; if the EOF packet
1838 ; is empty the latter condition can be true without
1839 ; having gotten the official EOF.
1840 cmp byte [si+tftp_goteof],0
1841 jne .ret ; Already EOF
1843 %if GPXE
1844 cmp word [si+tftp_localport], -1
1845 jne .get_packet_tftp
1846 call get_packet_gpxe
1847 jmp .ret
1848 .get_packet_tftp:
1849 %endif ; GPXE
1851 ; TFTP code...
1852 .packet_loop:
1853 ; Start by ACKing the previous packet; this should cause the
1854 ; next packet to be sent.
1855 mov cx,PKT_RETRY
1856 mov word [PktTimeout],PKT_TIMEOUT
1858 .send_ack: push cx ; <D> Retry count
1860 mov ax,[si+tftp_lastpkt]
1861 call ack_packet ; Send ACK
1863 ; We used to test the error code here, but sometimes
1864 ; PXE would return negative status even though we really
1865 ; did send the ACK. Now, just treat a failed send as
1866 ; a normally lost packet, and let it time out in due
1867 ; course of events.
1869 .send_ok: ; Now wait for packet.
1870 mov dx,[BIOS_timer] ; Get current time
1872 mov cx,[PktTimeout]
1873 .wait_data: push cx ; <E> Timeout
1874 push dx ; <F> Old time
1876 mov bx,[si+tftp_pktbuf]
1877 mov [pxe_udp_read_pkt.buffer],bx
1878 mov [pxe_udp_read_pkt.buffer+2],fs
1879 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1880 mov eax,[si+tftp_remoteip]
1881 mov [pxe_udp_read_pkt.sip],eax
1882 mov eax,[MyIP]
1883 mov [pxe_udp_read_pkt.dip],eax
1884 mov ax,[si+tftp_remoteport]
1885 mov [pxe_udp_read_pkt.rport],ax
1886 mov ax,[si+tftp_localport]
1887 mov [pxe_udp_read_pkt.lport],ax
1888 mov di,pxe_udp_read_pkt
1889 mov bx,PXENV_UDP_READ
1890 call pxenv
1891 jnc .recv_ok
1893 ; No packet, or receive failure
1894 mov dx,[BIOS_timer]
1895 pop ax ; <F> Old time
1896 pop cx ; <E> Timeout
1897 cmp ax,dx ; Same time -> don't advance timeout
1898 je .wait_data ; Same clock tick
1899 loop .wait_data ; Decrease timeout
1901 pop cx ; <D> Didn't get any, send another ACK
1902 shl word [PktTimeout],1 ; Exponential backoff
1903 loop .send_ack
1904 jmp kaboom ; Forget it...
1906 .recv_ok: pop dx ; <F>
1907 pop cx ; <E>
1909 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1910 jb .wait_data ; Bad size for a DATA packet
1912 mov bx,[si+tftp_pktbuf]
1913 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1914 jne .wait_data ; Then wait for something else
1916 mov ax,[si+tftp_lastpkt]
1917 xchg ah,al ; Host byte order
1918 inc ax ; Which packet are we waiting for?
1919 xchg ah,al ; Network byte order
1920 cmp [fs:bx+2],ax
1921 je .right_packet
1923 ; Wrong packet, ACK the packet and then try again
1924 ; This is presumably because the ACK got lost,
1925 ; so the server just resent the previous packet
1926 mov ax,[fs:bx+2]
1927 call ack_packet
1928 jmp .send_ok ; Reset timeout
1930 .right_packet: ; It's the packet we want. We're also EOF if the
1931 ; size < blocksize
1933 pop cx ; <D> Don't need the retry count anymore
1935 mov [si+tftp_lastpkt],ax ; Update last packet number
1937 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1938 sub cx,byte 4 ; Skip TFTP header
1940 ; Set pointer to data block
1941 lea ax,[bx+4] ; Data past TFTP header
1942 mov [si+tftp_dataptr],ax
1944 add [si+tftp_filepos],ecx
1945 mov [si+tftp_bytesleft],cx
1947 cmp cx,[si+tftp_blksize] ; Is it a full block?
1948 jb .last_block ; If not, it's EOF
1950 .ret:
1951 popad
1952 pop es
1956 .last_block: ; Last block - ACK packet immediately
1957 mov ax,[fs:bx+2]
1958 call ack_packet
1960 ; Make sure we know we are at end of file
1961 mov eax,[si+tftp_filepos]
1962 mov [si+tftp_filesize],eax
1963 mov byte [si+tftp_goteof],1
1965 jmp .ret
1968 ; ack_packet:
1970 ; Send ACK packet. This is a common operation and so is worth canning.
1972 ; Entry:
1973 ; SI = TFTP block
1974 ; AX = Packet # to ack (network byte order)
1975 ; Exit:
1976 ; All registers preserved
1978 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1980 ack_packet:
1981 pushad
1982 mov [ack_packet_buf+2],ax ; Packet number to ack
1983 mov ax,[si]
1984 mov [pxe_udp_write_pkt.lport],ax
1985 mov ax,[si+tftp_remoteport]
1986 mov [pxe_udp_write_pkt.rport],ax
1987 mov eax,[si+tftp_remoteip]
1988 mov [pxe_udp_write_pkt.sip],eax
1989 xor eax,[MyIP]
1990 and eax,[Netmask]
1991 jz .nogw
1992 mov eax,[Gateway]
1993 .nogw:
1994 mov [pxe_udp_write_pkt.gip],eax
1995 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1996 mov [pxe_udp_write_pkt.buffersize], word 4
1997 mov di,pxe_udp_write_pkt
1998 mov bx,PXENV_UDP_WRITE
1999 call pxenv
2000 popad
2003 %if GPXE
2005 ; Get a fresh packet from a gPXE socket; expects fs -> pktbuf_seg
2006 ; and ds:si -> socket structure
2008 ; Assumes CS == DS == ES.
2010 get_packet_gpxe:
2011 mov di,gpxe_file_read
2013 mov ax,[si+tftp_remoteport] ; gPXE filehandle
2014 mov [di+2],ax
2015 mov ax,[si+tftp_pktbuf]
2016 mov [di+6],ax
2017 mov [si+tftp_dataptr],ax
2018 mov [di+8],fs
2020 .again:
2021 mov word [di+4],PKTBUF_SIZE
2022 mov bx,PXENV_FILE_READ
2023 call pxenv
2024 jnc .ok ; Got data or EOF
2025 cmp word [di],PXENV_STATUS_TFTP_OPEN ; == EWOULDBLOCK
2026 je .again
2027 jmp kaboom ; Otherwise error...
2029 .ok:
2030 movzx eax,word [di+4] ; Bytes read
2031 mov [si+tftp_bytesleft],ax ; Bytes in buffer
2032 add [si+tftp_filepos],eax ; Position in file
2034 and ax,ax ; EOF?
2035 mov eax,[si+tftp_filepos]
2037 jnz .got_stuff
2039 ; We got EOF here, make sure the upper layers know
2040 mov [si+tftp_filesize],eax
2042 .got_stuff:
2043 ; If we're done here, close the file
2044 cmp [si+tftp_filesize],eax
2045 ja .done ; Not EOF, there is still data...
2047 ; Reuse the previous [es:di] structure since the
2048 ; relevant fields are all the same
2049 mov byte [si+tftp_goteof],1
2051 mov bx,PXENV_FILE_CLOSE
2052 call pxenv
2053 ; Ignore return...
2054 .done:
2056 %endif ; GPXE
2059 ; unload_pxe:
2061 ; This function unloads the PXE and UNDI stacks and unclaims
2062 ; the memory.
2064 unload_pxe:
2065 test byte [KeepPXE],01h ; Should we keep PXE around?
2066 jnz reset_pxe
2068 push ds
2069 push es
2071 mov ax,cs
2072 mov ds,ax
2073 mov es,ax
2075 mov si,new_api_unload
2076 cmp byte [APIVer+1],2 ; Major API version >= 2?
2077 jae .new_api
2078 mov si,old_api_unload
2079 .new_api:
2081 .call_loop: xor ax,ax
2082 lodsb
2083 and ax,ax
2084 jz .call_done
2085 xchg bx,ax
2086 mov di,pxe_unload_stack_pkt
2087 push di
2088 xor ax,ax
2089 mov cx,pxe_unload_stack_pkt_len >> 1
2090 rep stosw
2091 pop di
2092 call pxenv
2093 jc .cant_free
2094 mov ax,word [pxe_unload_stack_pkt.status]
2095 cmp ax,PXENV_STATUS_SUCCESS
2096 jne .cant_free
2097 jmp .call_loop
2099 .call_done:
2100 mov bx,0FF00h
2102 mov dx,[RealBaseMem]
2103 cmp dx,[BIOS_fbm] ; Sanity check
2104 jna .cant_free
2105 inc bx
2107 ; Check that PXE actually unhooked the INT 1Ah chain
2108 movzx eax,word [4*0x1a]
2109 movzx ecx,word [4*0x1a+2]
2110 shl ecx,4
2111 add eax,ecx
2112 shr eax,10
2113 cmp ax,dx ; Not in range
2114 jae .ok
2115 cmp ax,[BIOS_fbm]
2116 jae .cant_free
2117 ; inc bx
2119 .ok:
2120 mov [BIOS_fbm],dx
2121 .pop_ret:
2122 pop es
2123 pop ds
2126 .cant_free:
2127 mov si,cant_free_msg
2128 call writestr_early
2129 push ax
2130 xchg bx,ax
2131 call writehex4
2132 mov al,'-'
2133 call writechr
2134 pop ax
2135 call writehex4
2136 mov al,'-'
2137 call writechr
2138 mov eax,[4*0x1a]
2139 call writehex8
2140 call crlf
2141 jmp .pop_ret
2143 ; We want to keep PXE around, but still we should reset
2144 ; it to the standard bootup configuration
2145 reset_pxe:
2146 push es
2147 push cs
2148 pop es
2149 mov bx,PXENV_UDP_CLOSE
2150 mov di,pxe_udp_close_pkt
2151 call pxenv
2152 pop es
2156 ; gendotquad
2158 ; Take an IP address (in network byte order) in EAX and
2159 ; output a dotted quad string to ES:DI.
2160 ; DI points to terminal null at end of string on exit.
2162 gendotquad:
2163 push eax
2164 push cx
2165 mov cx,4
2166 .genchar:
2167 push eax
2168 cmp al,10 ; < 10?
2169 jb .lt10 ; If so, skip first 2 digits
2171 cmp al,100 ; < 100
2172 jb .lt100 ; If so, skip first digit
2174 aam 100
2175 ; Now AH = 100-digit; AL = remainder
2176 add ah,'0'
2177 mov [es:di],ah
2178 inc di
2180 .lt100:
2181 aam 10
2182 ; Now AH = 10-digit; AL = remainder
2183 add ah,'0'
2184 mov [es:di],ah
2185 inc di
2187 .lt10:
2188 add al,'0'
2189 stosb
2190 mov al,'.'
2191 stosb
2192 pop eax
2193 ror eax,8 ; Move next char into LSB
2194 loop .genchar
2195 dec di
2196 mov [es:di], byte 0
2197 pop cx
2198 pop eax
2201 ; uchexbytes/lchexbytes
2203 ; Take a number of bytes in memory and convert to upper/lower-case
2204 ; hexadecimal
2206 ; Input:
2207 ; DS:SI = input bytes
2208 ; ES:DI = output buffer
2209 ; CX = number of bytes
2210 ; Output:
2211 ; DS:SI = first byte after
2212 ; ES:DI = first byte after
2213 ; CX = 0
2215 ; Trashes AX, DX
2218 lchexbytes:
2219 mov dl,'a'-'9'-1
2220 jmp xchexbytes
2221 uchexbytes:
2222 mov dl,'A'-'9'-1
2223 xchexbytes:
2224 .loop:
2225 lodsb
2226 mov ah,al
2227 shr al,4
2228 call .outchar
2229 mov al,ah
2230 call .outchar
2231 loop .loop
2233 .outchar:
2234 and al,0Fh
2235 add al,'0'
2236 cmp al,'9'
2237 jna .done
2238 add al,dl
2239 .done:
2240 stosb
2244 ; pxe_get_cached_info
2246 ; Get a DHCP packet from the PXE stack into the trackbuf.
2248 ; Input:
2249 ; DL = packet type
2250 ; Output:
2251 ; CX = buffer size
2253 ; Assumes CS == DS == ES.
2255 pxe_get_cached_info:
2256 pushad
2257 mov si,get_packet_msg
2258 call writestr_early
2259 mov al,dl
2260 call writehex2
2261 call crlf
2262 mov di,pxe_bootp_query_pkt
2263 push di
2264 xor ax,ax
2265 stosw ; Status
2266 movzx ax,dl
2267 stosw ; Packet type
2268 mov ax,trackbufsize
2269 stosw ; Buffer size
2270 mov ax,trackbuf
2271 stosw ; Buffer offset
2272 xor ax,ax
2273 stosw ; Buffer segment
2275 pop di ; DI -> parameter set
2276 mov bx,PXENV_GET_CACHED_INFO
2277 call pxenv
2278 jc .err
2279 and ax,ax
2280 jnz .err
2282 popad
2283 mov cx,[pxe_bootp_query_pkt.buffersize]
2286 .err:
2287 mov si,err_pxefailed
2288 call writestr_early
2289 call writehex4
2290 call crlf
2291 jmp kaboom
2293 section .data
2294 get_packet_msg db 'Getting cached packet ', 0
2296 section .text
2298 ; ip_ok
2300 ; Tests an IP address in EAX for validity; return with ZF=1 for bad.
2301 ; We used to refuse class E, but class E addresses are likely to become
2302 ; assignable unicast addresses in the near future.
2304 ip_ok:
2305 push ax
2306 cmp eax,-1 ; Refuse the all-ones address
2307 jz .out
2308 and al,al ; Refuse network zero
2309 jz .out
2310 cmp al,127 ; Refuse loopback
2311 jz .out
2312 and al,0F0h
2313 cmp al,224 ; Refuse class D
2314 .out:
2315 pop ax
2319 ; parse_dhcp
2321 ; Parse a DHCP packet. This includes dealing with "overloaded"
2322 ; option fields (see RFC 2132, section 9.3)
2324 ; This should fill in the following global variables, if the
2325 ; information is present:
2327 ; MyIP - client IP address
2328 ; ServerIP - boot server IP address
2329 ; Netmask - network mask
2330 ; Gateway - default gateway router IP
2331 ; BootFile - boot file name
2332 ; DNSServers - DNS server IPs
2333 ; LocalDomain - Local domain name
2334 ; MACLen, MAC - Client identifier, if MACLen == 0
2336 ; This assumes the DHCP packet is in "trackbuf" and the length
2337 ; of the packet in in CX on entry.
2340 parse_dhcp:
2341 mov byte [OverLoad],0 ; Assume no overload
2342 mov eax, [trackbuf+bootp.yip]
2343 call ip_ok
2344 jz .noyip
2345 mov [MyIP], eax
2346 .noyip:
2347 mov eax, [trackbuf+bootp.sip]
2348 and eax, eax
2349 call ip_ok
2350 jz .nosip
2351 mov [ServerIP], eax
2352 .nosip:
2353 sub cx, bootp.options
2354 jbe .nooptions
2355 mov si, trackbuf+bootp.option_magic
2356 lodsd
2357 cmp eax, BOOTP_OPTION_MAGIC
2358 jne .nooptions
2359 call parse_dhcp_options
2360 .nooptions:
2361 mov si, trackbuf+bootp.bootfile
2362 test byte [OverLoad],1
2363 jz .nofileoverload
2364 mov cx,128
2365 call parse_dhcp_options
2366 jmp short .parsed_file
2367 .nofileoverload:
2368 cmp byte [si], 0
2369 jz .parsed_file ; No bootfile name
2370 mov di,BootFile
2371 mov cx,32
2372 rep movsd
2373 xor al,al
2374 stosb ; Null-terminate
2375 .parsed_file:
2376 mov si, trackbuf+bootp.sname
2377 test byte [OverLoad],2
2378 jz .nosnameoverload
2379 mov cx,64
2380 call parse_dhcp_options
2381 .nosnameoverload:
2385 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2386 ; size is CX -- some DHCP servers leave option fields unterminated
2387 ; in violation of the spec.
2389 ; For parse_some_dhcp_options, DH contains the minimum value for
2390 ; the option to recognize -- this is used to restrict parsing to
2391 ; PXELINUX-specific options only.
2393 parse_dhcp_options:
2394 xor dx,dx
2396 parse_some_dhcp_options:
2397 .loop:
2398 and cx,cx
2399 jz .done
2401 lodsb
2402 dec cx
2403 jz .done ; Last byte; must be PAD, END or malformed
2404 cmp al, 0 ; PAD option
2405 je .loop
2406 cmp al,255 ; END option
2407 je .done
2409 ; Anything else will have a length field
2410 mov dl,al ; DL <- option number
2411 xor ax,ax
2412 lodsb ; AX <- option length
2413 dec cx
2414 sub cx,ax ; Decrement bytes left counter
2415 jb .done ; Malformed option: length > field size
2417 cmp dl,dh ; Is the option value valid?
2418 jb .opt_done
2420 mov bx,dhcp_option_list
2421 .find_option:
2422 cmp bx,dhcp_option_list_end
2423 jae .opt_done
2424 cmp dl,[bx]
2425 je .found_option
2426 add bx,3
2427 jmp .find_option
2428 .found_option:
2429 pushad
2430 call [bx+1]
2431 popad
2433 ; Fall through
2434 ; Unknown option. Skip to the next one.
2435 .opt_done:
2436 add si,ax
2437 jmp .loop
2438 .done:
2441 section .data
2442 dhcp_option_list:
2443 section .text
2445 %macro dopt 2
2446 section .data
2447 db %1
2448 dw dopt_%2
2449 section .text
2450 dopt_%2:
2451 %endmacro
2454 ; Parse individual DHCP options. SI points to the option data and
2455 ; AX to the option length. DL contains the option number.
2456 ; All registers are saved around the routine.
2458 dopt 1, subnet_mask
2459 mov ebx,[si]
2460 mov [Netmask],ebx
2463 dopt 3, router
2464 mov ebx,[si]
2465 mov [Gateway],ebx
2468 dopt 6, dns_servers
2469 mov cx,ax
2470 shr cx,2
2471 cmp cl,DNS_MAX_SERVERS
2472 jna .oklen
2473 mov cl,DNS_MAX_SERVERS
2474 .oklen:
2475 mov di,DNSServers
2476 rep movsd
2477 mov [LastDNSServer],di
2480 dopt 16, local_domain
2481 mov bx,si
2482 add bx,ax
2483 xor ax,ax
2484 xchg [bx],al ; Zero-terminate option
2485 mov di,LocalDomain
2486 call dns_mangle ; Convert to DNS label set
2487 mov [bx],al ; Restore ending byte
2490 dopt 43, vendor_encaps
2491 mov dh,208 ; Only recognize PXELINUX options
2492 mov cx,ax ; Length of option = max bytes to parse
2493 call parse_some_dhcp_options ; Parse recursive structure
2496 dopt 52, option_overload
2497 mov bl,[si]
2498 mov [OverLoad],bl
2501 dopt 54, server
2502 mov eax,[si]
2503 cmp dword [ServerIP],0
2504 jne .skip ; Already have a next server IP
2505 call ip_ok
2506 jz .skip
2507 mov [ServerIP],eax
2508 .skip: ret
2510 dopt 61, client_identifier
2511 cmp ax,MAC_MAX ; Too long?
2512 ja .skip
2513 cmp ax,2 ; Too short?
2514 jb .skip
2515 cmp [MACLen],ah ; Only do this if MACLen == 0
2516 jne .skip
2517 push ax
2518 lodsb ; Client identifier type
2519 cmp al,[MACType]
2520 pop ax
2521 jne .skip ; Client identifier is not a MAC
2522 dec ax
2523 mov [MACLen],al
2524 mov di,MAC
2525 jmp dhcp_copyoption
2526 .skip: ret
2528 dopt 67, bootfile_name
2529 mov di,BootFile
2530 jmp dhcp_copyoption
2532 dopt 97, uuid_client_identifier
2533 cmp ax,17 ; type byte + 16 bytes UUID
2534 jne .skip
2535 mov dl,[si] ; Must have type 0 == UUID
2536 or dl,[HaveUUID] ; Capture only the first instance
2537 jnz .skip
2538 mov byte [HaveUUID],1 ; Got UUID
2539 mov di,UUIDType
2540 jmp dhcp_copyoption
2541 .skip: ret
2543 dopt 209, pxelinux_configfile
2544 mov di,ConfigName
2545 or byte [DHCPMagic],2 ; Got config file
2546 jmp dhcp_copyoption
2548 dopt 210, pxelinux_pathprefix
2549 mov di,PathPrefix
2550 or byte [DHCPMagic],4 ; Got path prefix
2551 jmp dhcp_copyoption
2553 dopt 211, pxelinux_reboottime
2554 cmp al,4
2555 jne .done
2556 mov ebx,[si]
2557 xchg bl,bh ; Convert to host byte order
2558 rol ebx,16
2559 xchg bl,bh
2560 mov [RebootTime],ebx
2561 or byte [DHCPMagic],8 ; Got RebootTime
2562 .done: ret
2564 ; Common code for copying an option verbatim
2565 ; Copies the option into ES:DI and null-terminates it.
2566 ; Returns with AX=0 and SI past the option.
2567 dhcp_copyoption:
2568 xchg cx,ax ; CX <- option length
2569 rep movsb
2570 xchg cx,ax ; AX <- 0
2571 stosb ; Null-terminate
2574 section .data
2575 dhcp_option_list_end:
2576 section .text
2578 section .data
2579 HaveUUID db 0
2580 uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
2581 section .text
2584 ; genipopt
2586 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2587 ; option into IPOption based on a DHCP packet in trackbuf.
2588 ; Assumes CS == DS == ES.
2590 genipopt:
2591 pushad
2592 mov di,IPOption
2593 mov eax,'ip='
2594 stosd
2595 dec di
2596 mov eax,[MyIP]
2597 call gendotquad
2598 mov al,':'
2599 stosb
2600 mov eax,[ServerIP]
2601 call gendotquad
2602 mov al,':'
2603 stosb
2604 mov eax,[Gateway]
2605 call gendotquad
2606 mov al,':'
2607 stosb
2608 mov eax,[Netmask]
2609 call gendotquad ; Zero-terminates its output
2610 sub di,IPOption
2611 mov [IPOptionLen],di
2612 popad
2616 ; Call the receive loop while idle. This is done mostly so we can respond to
2617 ; ARP messages, but perhaps in the future this can be used to do network
2618 ; console.
2620 ; hpa sez: people using automatic control on the serial port get very
2621 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2622 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2623 ; passed since the last poll, and reset this when a character is
2624 ; received (RESET_IDLE).
2626 ; Note: we only do this if pxe_detect_nic_type has cleared the
2627 ; "idle is noop" bit in feature_flags.
2629 %if HAVE_IDLE
2631 reset_idle:
2632 push ax
2633 mov ax,[cs:BIOS_timer]
2634 mov [cs:IdleTimer],ax
2635 pop ax
2638 check_for_arp:
2639 test byte [cs:feature_flags],2
2640 jnz .ret
2641 push ax
2642 mov ax,[cs:BIOS_timer]
2643 sub ax,[cs:IdleTimer]
2644 cmp ax,4
2645 pop ax
2646 jae .need_poll
2647 .ret: ret
2648 .need_poll: pushad
2649 push ds
2650 push es
2651 mov ax,cs
2652 mov ds,ax
2653 mov es,ax
2654 mov di,packet_buf
2655 mov [pxe_udp_read_pkt.buffer],di
2656 mov [pxe_udp_read_pkt.buffer+2],ds
2657 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2658 mov eax,[MyIP]
2659 mov [pxe_udp_read_pkt.dip],eax
2660 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2661 mov di,pxe_udp_read_pkt
2662 mov bx,PXENV_UDP_READ
2663 call pxenv
2664 ; Ignore result...
2665 pop es
2666 pop ds
2667 popad
2668 RESET_IDLE
2671 %endif ; HAVE_IDLE
2673 ; -----------------------------------------------------------------------------
2674 ; Common modules
2675 ; -----------------------------------------------------------------------------
2677 %include "getc.inc" ; getc et al
2678 %include "conio.inc" ; Console I/O
2679 %include "writestr.inc" ; String output
2680 writestr_early equ writestr
2681 %include "writehex.inc" ; Hexadecimal output
2682 %include "configinit.inc" ; Initialize configuration
2683 %include "parseconfig.inc" ; High-level config file handling
2684 %include "parsecmd.inc" ; Low-level config file handling
2685 %include "bcopy32.inc" ; 32-bit bcopy
2686 %include "loadhigh.inc" ; Load a file into high memory
2687 %include "font.inc" ; VGA font stuff
2688 %include "graphics.inc" ; VGA graphics
2689 %include "highmem.inc" ; High memory sizing
2690 %include "strcpy.inc" ; strcpy()
2691 %include "rawcon.inc" ; Console I/O w/o using the console functions
2692 %include "dnsresolv.inc" ; DNS resolver
2693 %include "pxeidle.inc" ; Idle mechanism
2694 %include "adv.inc" ; Auxillary Data Vector
2696 ; -----------------------------------------------------------------------------
2697 ; Begin data section
2698 ; -----------------------------------------------------------------------------
2700 section .data
2702 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2703 db CR, LF, 0
2704 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2705 bailmsg equ err_bootfailed
2706 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2707 err_pxefailed db 'PXE API call failed, error ', 0
2708 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2709 err_noconfig db 'Unable to locate configuration file', CR, LF, 0
2710 err_damage db 'TFTP server sent an incomprehesible reply', CR, LF, 0
2711 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2712 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2713 apiver_str db 'PXE API version is ',0
2714 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2715 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2716 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2717 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2718 undi_data_msg db 'UNDI data segment at: ',0
2719 undi_data_len_msg db 'UNDI data segment size: ',0
2720 undi_code_msg db 'UNDI code segment at: ',0
2721 undi_code_len_msg db 'UNDI code segment size: ',0
2722 cant_free_msg db 'Failed to free base memory, error ', 0
2723 notfound_msg db 'not found', CR, LF, 0
2724 myipaddr_msg db 'My IP address seems to be ',0
2725 tftpprefix_msg db 'TFTP prefix: ', 0
2726 localboot_msg db 'Booting from local disk...', CR, LF, 0
2727 trying_msg db 'Trying to load: ', 0
2728 fourbs_msg db BS, BS, BS, BS, 0
2729 default_str db 'default', 0
2730 syslinux_banner db CR, LF, 'PXELINUX ', VERSION_STR, ' ', DATE_STR, ' ', 0
2731 cfgprefix db 'pxelinux.cfg/' ; No final null!
2732 cfgprefix_len equ ($-cfgprefix)
2734 ; This one we make ourselves
2735 bootif_str db 'BOOTIF='
2736 bootif_str_len equ $-bootif_str
2738 ; Config file keyword table
2740 %include "keywords.inc"
2743 ; Extensions to search for (in *forward* order).
2744 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2746 align 4, db 0
2747 exten_table: db '.cbt' ; COMBOOT (specific)
2748 db '.0', 0, 0 ; PXE bootstrap program
2749 db '.com' ; COMBOOT (same as DOS)
2750 db '.c32' ; COM32
2751 exten_table_end:
2752 dd 0, 0 ; Need 8 null bytes here
2755 ; PXE unload sequences
2757 new_api_unload:
2758 db PXENV_UDP_CLOSE
2759 db PXENV_UNDI_SHUTDOWN
2760 db PXENV_UNLOAD_STACK
2761 db PXENV_STOP_UNDI
2762 db 0
2763 old_api_unload:
2764 db PXENV_UDP_CLOSE
2765 db PXENV_UNDI_SHUTDOWN
2766 db PXENV_UNLOAD_STACK
2767 db PXENV_UNDI_CLEANUP
2768 db 0
2771 ; PXE query packets partially filled in
2773 section .bss
2774 pxe_bootp_query_pkt:
2775 .status: resw 1 ; Status
2776 .packettype: resw 1 ; Boot server packet type
2777 .buffersize: resw 1 ; Packet size
2778 .buffer: resw 2 ; seg:off of buffer
2779 .bufferlimit: resw 1 ; Unused
2781 section .data
2782 pxe_udp_open_pkt:
2783 .status: dw 0 ; Status
2784 .sip: dd 0 ; Source (our) IP
2786 pxe_udp_close_pkt:
2787 .status: dw 0 ; Status
2789 pxe_udp_write_pkt:
2790 .status: dw 0 ; Status
2791 .sip: dd 0 ; Server IP
2792 .gip: dd 0 ; Gateway IP
2793 .lport: dw 0 ; Local port
2794 .rport: dw 0 ; Remote port
2795 .buffersize: dw 0 ; Size of packet
2796 .buffer: dw 0, 0 ; seg:off of buffer
2798 pxe_udp_read_pkt:
2799 .status: dw 0 ; Status
2800 .sip: dd 0 ; Source IP
2801 .dip: dd 0 ; Destination (our) IP
2802 .rport: dw 0 ; Remote port
2803 .lport: dw 0 ; Local port
2804 .buffersize: dw 0 ; Max packet size
2805 .buffer: dw 0, 0 ; seg:off of buffer
2807 %if GPXE
2809 gpxe_file_api_check:
2810 .status: dw 0 ; Status
2811 .size: dw 20 ; Size in bytes
2812 .magic: dd 0x91d447b2 ; Magic number
2813 .provider: dd 0
2814 .apimask: dd 0
2815 .flags: dd 0
2817 gpxe_file_open:
2818 .status: dw 0 ; Status
2819 .filehandle: dw 0 ; FileHandle
2820 .filename: dd 0 ; seg:off of FileName
2821 .reserved: dd 0
2823 gpxe_get_file_size:
2824 .status: dw 0 ; Status
2825 .filehandle: dw 0 ; FileHandle
2826 .filesize: dd 0 ; FileSize
2828 gpxe_file_read:
2829 .status: dw 0 ; Status
2830 .filehandle: dw 0 ; FileHandle
2831 .buffersize: dw 0 ; BufferSize
2832 .buffer: dd 0 ; seg:off of buffer
2834 %endif ; GPXE
2837 ; Misc initialized (data) variables
2839 alignb 4, db 0
2840 BaseStack dd StackBuf ; ESP of base stack
2841 dw 0 ; SS of base stack
2842 NextSocket dw 49152 ; Counter for allocating socket numbers
2843 KeepPXE db 0 ; Should PXE be kept around?
2846 ; TFTP commands
2848 tftp_tail db 'octet', 0 ; Octet mode
2849 tsize_str db 'tsize' ,0 ; Request size
2850 tsize_len equ ($-tsize_str)
2851 db '0', 0
2852 blksize_str db 'blksize', 0 ; Request large blocks
2853 blksize_len equ ($-blksize_str)
2854 asciidec TFTP_LARGEBLK
2855 db 0
2856 tftp_tail_len equ ($-tftp_tail)
2858 alignb 2, db 0
2860 ; Options negotiation parsing table (string pointer, string len, offset
2861 ; into socket structure)
2863 tftp_opt_table:
2864 dw tsize_str, tsize_len, tftp_filesize
2865 dw blksize_str, blksize_len, tftp_blksize
2866 tftp_opts equ ($-tftp_opt_table)/6
2869 ; Error packet to return on options negotiation error
2871 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2872 dw TFTP_EOPTNEG ; ERROR 8: bad options
2873 db 'tsize option required', 0 ; Error message
2874 tftp_opt_err_len equ ($-tftp_opt_err)
2876 alignb 4, db 0
2877 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2880 ; IP information (initialized to "unknown" values)
2881 MyIP dd 0 ; My IP address
2882 ServerIP dd 0 ; IP address of boot server
2883 Netmask dd 0 ; Netmask of this subnet
2884 Gateway dd 0 ; Default router
2885 ServerPort dw TFTP_PORT ; TFTP server port
2888 ; Variables that are uninitialized in SYSLINUX but initialized here
2890 alignb 4, db 0
2891 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2892 BufSafeBytes dw trackbufsize ; = how many bytes?
2893 %ifndef DEPEND
2894 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2895 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2896 %endif
2897 %endif