mboot.c32:load ELF segments from the segment header
[syslinux.git] / pxelinux.asm
blob9ff832c35e44cb990b963c076ae75f84b64cffe3
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; $Id$
3 ; ****************************************************************************
5 ; pxelinux.asm
7 ; A program to boot Linux kernels off a TFTP server using the Intel PXE
8 ; network booting API. It is based on the SYSLINUX boot loader for
9 ; MS-DOS floppies.
11 ; Copyright (C) 1994-2005 H. Peter Anvin
13 ; This program is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
16 ; Boston MA 02111-1307, USA; either version 2 of the License, or
17 ; (at your option) any later version; incorporated herein by reference.
19 ; ****************************************************************************
21 %define IS_PXELINUX 1
22 %include "macros.inc"
23 %include "config.inc"
24 %include "kernel.inc"
25 %include "bios.inc"
26 %include "tracers.inc"
27 %include "pxe.inc"
28 %include "layout.inc"
31 ; Some semi-configurable constants... change on your own risk.
33 my_id equ pxelinux_id
34 FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
35 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
36 NULLFILE equ 0 ; Zero byte == null file name
37 NULLOFFSET equ 4 ; Position in which to look
38 REBOOT_TIME equ 5*60 ; If failure, time until full reset
39 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
40 MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
41 MAX_OPEN equ (1 << MAX_OPEN_LG2)
42 PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
43 TFTP_PORT equ htons(69) ; Default TFTP port
44 PKT_RETRY equ 6 ; Packet transmit retry count
45 PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
46 ; Desired TFTP block size
47 ; For Ethernet MTU is normally 1500. Unfortunately there seems to
48 ; be a fair number of networks with "substandard" MTUs which break.
49 ; The code assumes TFTP_LARGEBLK <= 2K.
50 TFTP_MTU equ 1472
51 TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
52 ; Standard TFTP block size
53 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
54 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
55 %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
57 SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
58 SECTOR_SIZE equ TFTP_BLOCKSIZE
61 ; This is what we need to do when idle
63 %define HAVE_IDLE 1 ; idle is not a noop
65 %macro RESET_IDLE 0
66 call reset_idle
67 %endmacro
68 %macro DO_IDLE 0
69 call check_for_arp
70 %endmacro
73 ; TFTP operation codes
75 TFTP_RRQ equ htons(1) ; Read request
76 TFTP_WRQ equ htons(2) ; Write request
77 TFTP_DATA equ htons(3) ; Data packet
78 TFTP_ACK equ htons(4) ; ACK packet
79 TFTP_ERROR equ htons(5) ; ERROR packet
80 TFTP_OACK equ htons(6) ; OACK packet
83 ; TFTP error codes
85 TFTP_EUNDEF equ htons(0) ; Unspecified error
86 TFTP_ENOTFOUND equ htons(1) ; File not found
87 TFTP_EACCESS equ htons(2) ; Access violation
88 TFTP_ENOSPACE equ htons(3) ; Disk full
89 TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
90 TFTP_EBADID equ htons(5) ; Unknown transfer
91 TFTP_EEXISTS equ htons(6) ; File exists
92 TFTP_ENOUSER equ htons(7) ; No such user
93 TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
96 ; The following structure is used for "virtual kernels"; i.e. LILO-style
97 ; option labels. The options we permit here are `kernel' and `append
98 ; Since there is no room in the bottom 64K for all of these, we
99 ; stick them at vk_seg:0000 and copy them down before we need them.
101 struc vkernel
102 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
103 vk_rname: resb FILENAME_MAX ; Real name
104 vk_ipappend: resb 1 ; "IPAPPEND" flag
105 resb 1 ; Pad
106 vk_appendlen: resw 1
107 alignb 4
108 vk_append: resb max_cmd_len+1 ; Command line
109 alignb 4
110 vk_end: equ $ ; Should be <= vk_size
111 endstruc
114 ; Segment assignments in the bottom 640K
115 ; 0000h - main code/data segment (and BIOS segment)
117 real_mode_seg equ 4000h
118 vk_seg equ 3000h ; Virtual kernels
119 xfer_buf_seg equ 2000h ; Bounce buffer for I/O to high mem
120 pktbuf_seg equ 1000h ; Packet buffers segments
121 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
124 ; BOOTP/DHCP packet pattern
126 struc bootp_t
127 bootp:
128 .opcode resb 1 ; BOOTP/DHCP "opcode"
129 .hardware resb 1 ; ARP hardware type
130 .hardlen resb 1 ; Hardware address length
131 .gatehops resb 1 ; Used by forwarders
132 .ident resd 1 ; Transaction ID
133 .seconds resw 1 ; Seconds elapsed
134 .flags resw 1 ; Broadcast flags
135 .cip resd 1 ; Client IP
136 .yip resd 1 ; "Your" IP
137 .sip resd 1 ; Next server IP
138 .gip resd 1 ; Relay agent IP
139 .macaddr resb 16 ; Client MAC address
140 .sname resb 64 ; Server name (optional)
141 .bootfile resb 128 ; Boot file name
142 .option_magic resd 1 ; Vendor option magic cookie
143 .options resb 1260 ; Vendor options
144 endstruc
146 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
149 ; TFTP connection data structure. Each one of these corresponds to a local
150 ; UDP port. The size of this structure must be a power of 2.
151 ; HBO = host byte order; NBO = network byte order
152 ; (*) = written by options negotiation code, must be dword sized
154 struc open_file_t
155 tftp_localport resw 1 ; Local port number (0 = not in use)
156 tftp_remoteport resw 1 ; Remote port number
157 tftp_remoteip resd 1 ; Remote IP address
158 tftp_filepos resd 1 ; Bytes downloaded (including buffer)
159 tftp_filesize resd 1 ; Total file size(*)
160 tftp_blksize resd 1 ; Block size for this connection(*)
161 tftp_bytesleft resw 1 ; Unclaimed data bytes
162 tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
163 tftp_dataptr resw 1 ; Pointer to available data
164 resw 2 ; Currently unusued
165 ; At end since it should not be zeroed on socked close
166 tftp_pktbuf resw 1 ; Packet buffer offset
167 endstruc
168 %ifndef DEPEND
169 %if (open_file_t_size & (open_file_t_size-1))
170 %error "open_file_t is not a power of 2"
171 %endif
172 %endif
174 ; ---------------------------------------------------------------------------
175 ; BEGIN CODE
176 ; ---------------------------------------------------------------------------
179 ; Memory below this point is reserved for the BIOS and the MBR
181 section .earlybss
182 trackbufsize equ 8192
183 trackbuf resb trackbufsize ; Track buffer goes here
184 getcbuf resb trackbufsize
185 ; ends at 4800h
187 ; Put some large buffers here, before RBFG_brainfuck,
188 ; where we can still carefully control the address
189 ; assignments...
191 alignb open_file_t_size
192 Files resb MAX_OPEN*open_file_t_size
194 alignb FILENAME_MAX
195 BootFile resb 256 ; Boot file from DHCP packet
196 ConfigServer resd 1 ; Null prefix for mangled config name
197 ConfigName resb 256-4 ; Configuration file from DHCP option
198 PathPrefix resb 256 ; Path prefix derived from boot file
199 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
200 IPOption resb 80 ; ip= option buffer
201 InitStack resd 1 ; Pointer to reset stack (SS:SP)
202 PXEStack resd 1 ; Saved stack during PXE call
204 ; Warning here: RBFG build 22 randomly overwrites memory location
205 ; [0x5680,0x576c), possibly more. It seems that it gets confused and
206 ; screws up the pointer to its own internal packet buffer and starts
207 ; writing a received ARP packet into low memory.
208 RBFG_brainfuck resb 0E00h
210 section .latebss
211 alignb 4
212 RebootTime resd 1 ; Reboot timeout, if set by option
213 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
214 APIVer resw 1 ; PXE API version found
215 IPOptionLen resw 1 ; Length of IPOption
216 IdleTimer resw 1 ; Time to check for ARP?
217 LocalBootType resw 1 ; Local boot return code
218 PktTimeout resw 1 ; Timeout for current packet
219 RealBaseMem resw 1 ; Amount of DOS memory after freeing
220 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
222 ; The relative position of these fields matter!
223 MACLen resb 1 ; MAC address len
224 MACType resb 1 ; MAC address type
225 MAC resb 16 ; Actual MAC address
226 BOOTIFStr resb 7 ; Space for "BOOTIF="
227 MACStr resb 3*17 ; MAC address as a string
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 alignb 16
242 packet_buf resb 2048 ; Transfer packet
243 packet_buf_size equ $-packet_buf
246 ; Constants for the xfer_buf_seg
248 ; The xfer_buf_seg is also used to store message file buffers. We
249 ; need two trackbuffers (text and graphics), plus a work buffer
250 ; for the graphics decompressor.
252 xbs_textbuf equ 0 ; Also hard-coded, do not change
253 xbs_vgabuf equ trackbufsize
254 xbs_vgatmpbuf equ 2*trackbufsize
256 section .text
258 ; PXELINUX needs more BSS than the other derivatives;
259 ; therefore we relocate it from 7C00h on startup.
261 StackBuf equ $ ; Base of stack if we use our own
264 ; Primary entry point.
266 bootsec equ $
267 _start:
268 pushfd ; Paranoia... in case of return to PXE
269 pushad ; ... save as much state as possible
270 push ds
271 push es
272 push fs
273 push gs
275 xor ax,ax
276 mov ds,ax
277 mov es,ax
279 %ifndef DEPEND
280 %if TEXT_START != 0x7c00
281 ; This is uglier than it should be, but works around
282 ; some NASM 0.98.38 bugs.
283 mov di,section..bcopy32.start
284 add di,__bcopy_size-4
285 lea si,[di-(TEXT_START-7C00h)]
286 lea cx,[di-(TEXT_START-4)]
287 shr cx,2
288 std ; Overlapping areas, copy backwards
289 rep movsd
290 %endif
291 %endif
292 jmp 0:_start1 ; Canonicalize address
293 _start1:
294 mov bp,sp
295 les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
297 ; That is all pushed onto the PXE stack. Save the pointer
298 ; to it and switch to an internal stack.
299 mov [InitStack],sp
300 mov [InitStack+2],ss
302 %if USE_PXE_PROVIDED_STACK
303 ; Apparently some platforms go bonkers if we
304 ; set up our own stack...
305 mov [BaseStack],sp
306 mov [BaseStack+4],ss
307 %endif
309 cli ; Paranoia
310 lss esp,[BaseStack]
312 sti ; Stack set up and ready
313 cld ; Copy upwards
316 ; Initialize screen (if we're using one)
318 ; Now set up screen parameters
319 call adjust_screen
321 ; Wipe the F-key area
322 mov al,NULLFILE
323 mov di,FKeyName
324 mov cx,10*(1 << FILENAME_MAX_LG2)
325 push es ; Save ES -> PXE structure
326 push ds ; ES <- DS
327 pop es
328 rep stosb
329 pop es ; Restore ES
332 ; Tell the user we got this far
334 mov si,syslinux_banner
335 call writestr
337 mov si,copyright_str
338 call writestr
341 ; Assume API version 2.1, in case we find the !PXE structure without
342 ; finding the PXENV+ structure. This should really look at the Base
343 ; Code ROM ID structure in have_pxe, but this is adequate for now --
344 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
345 ; about higher versions than that.
347 mov word [APIVer],0201h
350 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
351 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
352 ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
353 ; We should make that the second test, and not trash ES:BX...
355 cmp dword [es:bx], '!PXE'
356 je have_pxe
358 ; Uh-oh, not there... try plan B
359 mov ax, 5650h
360 %if USE_PXE_PROVIDED_STACK == 0
361 lss sp,[InitStack]
362 %endif
363 int 1Ah ; May trash regs
364 %if USE_PXE_PROVIDED_STACK == 0
365 lss esp,[BaseStack]
366 %endif
368 jc no_pxe
369 cmp ax,564Eh
370 jne no_pxe
372 ; Okay, that gave us the PXENV+ structure, find !PXE
373 ; structure from that (if available)
374 cmp dword [es:bx], 'PXEN'
375 jne no_pxe
376 cmp word [es:bx+4], 'V+'
377 je have_pxenv
379 ; Nothing there either. Last-ditch: scan memory
380 call memory_scan_for_pxe_struct ; !PXE scan
381 jnc have_pxe
382 call memory_scan_for_pxenv_struct ; PXENV+ scan
383 jnc have_pxenv
385 no_pxe: mov si,err_nopxe
386 call writestr
387 jmp kaboom
389 have_pxenv:
390 mov [StrucPtr],bx
391 mov [StrucPtr+2],es
393 mov si,found_pxenv
394 call writestr
396 mov si,apiver_str
397 call writestr
398 mov ax,[es:bx+6]
399 mov [APIVer],ax
400 call writehex4
401 call crlf
403 cmp ax,0201h ; API version 2.1 or higher
404 jb old_api
405 mov si,bx
406 mov ax,es
407 les bx,[es:bx+28h] ; !PXE structure pointer
408 cmp dword [es:bx],'!PXE'
409 je have_pxe
411 ; Nope, !PXE structure missing despite API 2.1+, or at least
412 ; the pointer is missing. Do a last-ditch attempt to find it.
413 call memory_scan_for_pxe_struct
414 jnc have_pxe
416 ; Otherwise, no dice, use PXENV+ structure
417 mov bx,si
418 mov es,ax
420 old_api: ; Need to use a PXENV+ structure
421 mov si,using_pxenv_msg
422 call writestr
424 mov eax,[es:bx+0Ah] ; PXE RM API
425 mov [PXENVEntry],eax
427 mov si,undi_data_msg
428 call writestr
429 mov ax,[es:bx+20h]
430 call writehex4
431 call crlf
432 mov si,undi_data_len_msg
433 call writestr
434 mov ax,[es:bx+22h]
435 call writehex4
436 call crlf
437 mov si,undi_code_msg
438 call writestr
439 mov ax,[es:bx+24h]
440 call writehex4
441 call crlf
442 mov si,undi_code_len_msg
443 call writestr
444 mov ax,[es:bx+26h]
445 call writehex4
446 call crlf
448 ; Compute base memory size from PXENV+ structure
449 xor esi,esi
450 movzx eax,word [es:bx+20h] ; UNDI data seg
451 cmp ax,[es:bx+24h] ; UNDI code seg
452 ja .use_data
453 mov ax,[es:bx+24h]
454 mov si,[es:bx+26h]
455 jmp short .combine
456 .use_data:
457 mov si,[es:bx+22h]
458 .combine:
459 shl eax,4
460 add eax,esi
461 shr eax,10 ; Convert to kilobytes
462 mov [RealBaseMem],ax
464 mov si,pxenventry_msg
465 call writestr
466 mov ax,[PXENVEntry+2]
467 call writehex4
468 mov al,':'
469 call writechr
470 mov ax,[PXENVEntry]
471 call writehex4
472 call crlf
473 jmp have_entrypoint
475 have_pxe:
476 mov [StrucPtr],bx
477 mov [StrucPtr+2],es
479 mov eax,[es:bx+10h]
480 mov [PXEEntry],eax
482 mov si,undi_data_msg
483 call writestr
484 mov eax,[es:bx+2Ah]
485 call writehex8
486 call crlf
487 mov si,undi_data_len_msg
488 call writestr
489 mov ax,[es:bx+2Eh]
490 call writehex4
491 call crlf
492 mov si,undi_code_msg
493 call writestr
494 mov ax,[es:bx+32h]
495 call writehex8
496 call crlf
497 mov si,undi_code_len_msg
498 call writestr
499 mov ax,[es:bx+36h]
500 call writehex4
501 call crlf
503 ; Compute base memory size from !PXE structure
504 xor esi,esi
505 mov eax,[es:bx+2Ah]
506 cmp eax,[es:bx+32h]
507 ja .use_data
508 mov eax,[es:bx+32h]
509 mov si,[es:bx+36h]
510 jmp short .combine
511 .use_data:
512 mov si,[es:bx+2Eh]
513 .combine:
514 add eax,esi
515 shr eax,10
516 mov [RealBaseMem],ax
518 mov si,pxeentry_msg
519 call writestr
520 mov ax,[PXEEntry+2]
521 call writehex4
522 mov al,':'
523 call writechr
524 mov ax,[PXEEntry]
525 call writehex4
526 call crlf
528 have_entrypoint:
529 push cs
530 pop es ; Restore CS == DS == ES
533 ; Network-specific initialization
535 xor ax,ax
536 mov [LocalDomain],al ; No LocalDomain received
539 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
540 ; address). This lives in the DHCPACK packet (query info 2).
542 query_bootp:
543 mov di,pxe_bootp_query_pkt_2
544 mov bx,PXENV_GET_CACHED_INFO
546 call pxenv
547 push word [pxe_bootp_query_pkt_2.status]
548 jc .pxe_err1
549 cmp ax,byte 0
550 je .pxe_ok
551 .pxe_err1:
552 mov di,pxe_bootp_size_query_pkt
553 mov bx,PXENV_GET_CACHED_INFO
555 call pxenv
556 jc .pxe_err
557 .pxe_size:
558 mov ax,[pxe_bootp_size_query_pkt.buffersize]
559 call writehex4
560 call crlf
562 .pxe_err:
563 mov si,err_pxefailed
564 call writestr
565 call writehex4
566 mov al, ' '
567 call writechr
568 pop ax ; Status
569 call writehex4
570 call crlf
571 jmp kaboom ; We're dead
573 .pxe_ok:
574 pop cx ; Forget status
575 mov cx,[pxe_bootp_query_pkt_2.buffersize]
576 call parse_dhcp ; Parse DHCP packet
578 ; Save away MAC address (assume this is in query info 2. If this
579 ; turns out to be problematic it might be better getting it from
580 ; the query info 1 packet.)
582 .save_mac:
583 movzx cx,byte [trackbuf+bootp.hardlen]
584 mov [MACLen],cl
585 mov al,[trackbuf+bootp.hardware]
586 mov [MACType],al
587 mov si,trackbuf+bootp.macaddr
588 mov di,MAC
589 push cx
590 rep movsb
591 mov cx,MAC+16
592 sub cx,di
593 xor ax,ax
594 rep stosb
596 mov si,bootif_str
597 mov di,BOOTIFStr
598 mov cx,bootif_str_len
599 rep movsb
601 pop cx
602 mov si,MACType
603 inc cx
604 mov bx,hextbl_lower
605 .hexify_mac:
606 lodsb
607 mov ah,al
608 shr al,4
609 xlatb
610 stosb
611 mov al,ah
612 and al,0Fh
613 xlatb
614 stosb
615 mov al,'-'
616 stosb
617 loop .hexify_mac
618 mov [di-1],byte 0 ; Null-terminate and strip final colon
621 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
622 ; packet (query info 3).
624 mov [pxe_bootp_size_query_pkt.packettype], byte 3
626 mov di,pxe_bootp_query_pkt_3
627 mov bx,PXENV_GET_CACHED_INFO
629 call pxenv
630 push word [pxe_bootp_query_pkt_3.status]
631 jc .pxe_err1
632 cmp ax,byte 0
633 jne .pxe_err1
635 ; Packet loaded OK...
636 pop cx ; Forget status
637 mov cx,[pxe_bootp_query_pkt_3.buffersize]
638 call parse_dhcp ; Parse DHCP packet
640 ; Generate ip= option
642 call genipopt
645 ; Print IP address
647 mov eax,[MyIP]
648 mov di,DotQuadBuf
649 push di
650 call gendotquad ; This takes network byte order input
652 xchg ah,al ; Convert to host byte order
653 ror eax,16 ; (BSWAP doesn't work on 386)
654 xchg ah,al
656 mov si,myipaddr_msg
657 call writestr
658 call writehex8
659 mov al,' '
660 call writechr
661 pop si ; DotQuadBuf
662 call writestr
663 call crlf
665 mov si,IPOption
666 call writestr
667 call crlf
670 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
671 ; if we didn't get the magic enable, do not recognize any other options.
673 check_dhcp_magic:
674 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
675 jnz .got_magic
676 mov byte [DHCPMagic], 0 ; If not, kill all other options
677 .got_magic:
681 ; Initialize UDP stack
683 udp_init:
684 mov eax,[MyIP]
685 mov [pxe_udp_open_pkt.sip],eax
686 mov di,pxe_udp_open_pkt
687 mov bx,PXENV_UDP_OPEN
688 call pxenv
689 jc .failed
690 cmp word [pxe_udp_open_pkt.status], byte 0
691 je .success
692 .failed: mov si,err_udpinit
693 call writestr
694 jmp kaboom
695 .success:
698 ; Common initialization code
700 %include "init.inc"
701 %include "cpuinit.inc"
704 ; Now we're all set to start with our *real* business. First load the
705 ; configuration file (if any) and parse it.
707 ; In previous versions I avoided using 32-bit registers because of a
708 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
709 ; random. I figure, though, that if there are any of those still left
710 ; they probably won't be trying to install Linux on them...
712 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
713 ; to take'm out. In fact, we may want to put them back if we're going
714 ; to boot ELKS at some point.
718 ; Store standard filename prefix
720 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
721 jnz .got_prefix
722 mov si,BootFile
723 mov di,PathPrefix
725 call strcpy
726 mov cx,di
727 sub cx,PathPrefix+1
729 lea si,[di-2] ; Skip final null!
730 .find_alnum: lodsb
731 or al,20h
732 cmp al,'.' ; Count . or - as alphanum
733 je .alnum
734 cmp al,'-'
735 je .alnum
736 cmp al,'0'
737 jb .notalnum
738 cmp al,'9'
739 jbe .alnum
740 cmp al,'a'
741 jb .notalnum
742 cmp al,'z'
743 ja .notalnum
744 .alnum: loop .find_alnum
745 dec si
746 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
748 .got_prefix:
749 mov si,tftpprefix_msg
750 call writestr
751 mov si,PathPrefix
752 call writestr
753 call crlf
756 ; Load configuration file
758 find_config:
761 ; Begin looking for configuration file
763 config_scan:
764 mov di,ConfigServer
765 xor eax,eax
766 stosd ; The config file is always from the server
768 test byte [DHCPMagic], 02h
769 jz .no_option
771 ; We got a DHCP option, try it first
772 mov si,trying_msg
773 call writestr
774 ; mov di,ConfigName ; - already the case
775 mov si,di
776 call writestr
777 call crlf
778 mov di,ConfigServer
779 call open
780 jnz .success
782 .no_option:
783 mov di,ConfigName
784 mov si,cfgprefix
785 mov cx,cfgprefix_len
786 rep movsb
788 ; Try loading by MAC address
789 ; Have to guess config file name
790 push di
791 mov si,MACStr
792 mov cx,(3*17+1)/2
793 rep movsw
794 mov si,trying_msg
795 call writestr
796 mov di,ConfigName
797 mov si,di
798 call writestr
799 call crlf
800 mov di,ConfigServer
801 call open
802 pop di
803 jnz .success
805 .scan_ip:
806 mov cx,8
807 mov eax,[MyIP]
808 xchg ah,al ; Convert to host byte order
809 ror eax,16
810 xchg ah,al
811 .hexify_loop: rol eax,4
812 push eax
813 and al,0Fh
814 cmp al,10
815 jae .high
816 .low: add al,'0'
817 jmp short .char
818 .high: add al,'A'-10
819 .char: stosb
820 pop eax
821 loop .hexify_loop
823 mov cx,9 ; Up to 9 attempts
825 .tryagain: mov byte [di],0
826 cmp cx,byte 1
827 jne .not_default
828 pusha
829 mov si,default_str
830 mov cx,default_len
831 rep movsb ; Copy "default" string
832 popa
833 .not_default: pusha
834 mov si,trying_msg
835 call writestr
836 mov di,ConfigName
837 mov si,di
838 call writestr
839 call crlf
840 mov di,ConfigServer
841 call open
842 popa
843 jnz .success
844 dec di
845 loop .tryagain
847 jmp no_config_file
849 .success:
852 ; Now we have the config file open. Parse the config file and
853 ; run the user interface.
855 %include "ui.inc"
858 ; Linux kernel loading code is common. However, we need to define
859 ; a couple of helper macros...
862 ; Handle "ipappend" option
863 %define HAVE_SPECIAL_APPEND
864 %macro SPECIAL_APPEND 0
865 test byte [IPAppend],01h ; ip=
866 jz .noipappend1
867 mov si,IPOption
868 mov cx,[IPOptionLen]
869 rep movsb
870 mov al,' '
871 stosb
872 .noipappend1:
873 test byte [IPAppend],02h
874 jz .noipappend2
875 mov si,BOOTIFStr
876 call strcpy
877 mov byte [es:di-1],' ' ; Replace null with space
878 .noipappend2:
879 %endmacro
881 ; Unload PXE stack
882 %define HAVE_UNLOAD_PREP
883 %macro UNLOAD_PREP 0
884 call unload_pxe
885 %endmacro
887 %include "runkernel.inc"
890 ; COMBOOT-loading code
892 %include "comboot.inc"
893 %include "com32.inc"
894 %include "cmdline.inc"
897 ; Boot sector loading code
899 %include "bootsect.inc"
902 ; Boot to the local disk by returning the appropriate PXE magic.
903 ; AX contains the appropriate return code.
905 local_boot:
906 push cs
907 pop ds
908 mov [LocalBootType],ax
909 call vgaclearmode
910 mov si,localboot_msg
911 call writestr
912 ; Restore the environment we were called with
913 lss sp,[InitStack]
914 pop gs
915 pop fs
916 pop es
917 pop ds
918 popad
919 mov ax,[cs:LocalBootType]
920 popfd
921 retf ; Return to PXE
924 ; Abort loading code
926 %include "abort.inc"
929 ; kaboom: write a message and bail out. Wait for quite a while,
930 ; or a user keypress, then do a hard reboot.
932 kaboom:
933 mov ax,cs
934 mov es,ax
935 mov ds,ax
936 lss esp,[BaseStack]
938 .patch: mov si,bailmsg
939 call writestr ; Returns with AL = 0
940 .drain: call pollchar
941 jz .drained
942 call getchar
943 jmp short .drain
944 .drained:
945 mov edi,[RebootTime]
946 mov al,[DHCPMagic]
947 and al,09h ; Magic+Timeout
948 cmp al,09h
949 je .time_set
950 mov edi,REBOOT_TIME
951 .time_set:
952 mov cx,18
953 .wait1: push cx
954 mov ecx,edi
955 .wait2: mov dx,[BIOS_timer]
956 .wait3: call pollchar
957 jnz .keypress
958 cmp dx,[BIOS_timer]
959 je .wait3
960 loop .wait2,ecx
961 mov al,'.'
962 call writechr
963 pop cx
964 loop .wait1
965 .keypress:
966 call crlf
967 mov word [BIOS_magic],0 ; Cold reboot
968 jmp 0F000h:0FFF0h ; Reset vector address
971 ; memory_scan_for_pxe_struct:
973 ; If none of the standard methods find the !PXE structure, look for it
974 ; by scanning memory.
976 ; On exit, if found:
977 ; CF = 0, ES:BX -> !PXE structure
978 ; Otherwise CF = 1, all registers saved
980 memory_scan_for_pxe_struct:
981 push ds
982 pusha
983 mov ax,cs
984 mov ds,ax
985 mov si,trymempxe_msg
986 call writestr
987 mov ax,[BIOS_fbm] ; Starting segment
988 shl ax,(10-4) ; Kilobytes -> paragraphs
989 ; mov ax,01000h ; Start to look here
990 dec ax ; To skip inc ax
991 .mismatch:
992 inc ax
993 cmp ax,0A000h ; End of memory
994 jae .not_found
995 call writehex4
996 mov si,fourbs_msg
997 call writestr
998 mov es,ax
999 mov edx,[es:0]
1000 cmp edx,'!PXE'
1001 jne .mismatch
1002 movzx cx,byte [es:4] ; Length of structure
1003 cmp cl,08h ; Minimum length
1004 jb .mismatch
1005 push ax
1006 xor ax,ax
1007 xor si,si
1008 .checksum: es lodsb
1009 add ah,al
1010 loop .checksum
1011 pop ax
1012 jnz .mismatch ; Checksum must == 0
1013 .found: mov bp,sp
1014 xor bx,bx
1015 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
1016 mov ax,es
1017 call writehex4
1018 call crlf
1019 popa
1020 pop ds
1023 .not_found: mov si,notfound_msg
1024 call writestr
1025 popa
1026 pop ds
1031 ; memory_scan_for_pxenv_struct:
1033 ; If none of the standard methods find the PXENV+ structure, look for it
1034 ; by scanning memory.
1036 ; On exit, if found:
1037 ; CF = 0, ES:BX -> PXENV+ structure
1038 ; Otherwise CF = 1, all registers saved
1040 memory_scan_for_pxenv_struct:
1041 pusha
1042 mov si,trymempxenv_msg
1043 call writestr
1044 ; mov ax,[BIOS_fbm] ; Starting segment
1045 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1046 mov ax,01000h ; Start to look here
1047 dec ax ; To skip inc ax
1048 .mismatch:
1049 inc ax
1050 cmp ax,0A000h ; End of memory
1051 jae .not_found
1052 mov es,ax
1053 mov edx,[es:0]
1054 cmp edx,'PXEN'
1055 jne .mismatch
1056 mov dx,[es:4]
1057 cmp dx,'V+'
1058 jne .mismatch
1059 movzx cx,byte [es:8] ; Length of structure
1060 cmp cl,26h ; Minimum length
1061 jb .mismatch
1062 xor ax,ax
1063 xor si,si
1064 .checksum: es lodsb
1065 add ah,al
1066 loop .checksum
1067 and ah,ah
1068 jnz .mismatch ; Checksum must == 0
1069 .found: mov bp,sp
1070 mov [bp+8],bx ; Save BX into stack frame
1071 mov ax,bx
1072 call writehex4
1073 call crlf
1076 .not_found: mov si,notfound_msg
1077 call writestr
1078 popad
1083 ; searchdir:
1085 ; Open a TFTP connection to the server
1087 ; On entry:
1088 ; DS:DI = mangled filename
1089 ; If successful:
1090 ; ZF clear
1091 ; SI = socket pointer
1092 ; DX:AX = file length in bytes
1093 ; If unsuccessful
1094 ; ZF set
1097 searchdir:
1098 push es
1099 push bx
1100 push cx
1101 mov ax,ds
1102 mov es,ax
1103 mov si,di
1104 push bp
1105 mov bp,sp
1107 call allocate_socket
1108 jz .ret
1110 mov ax,PKT_RETRY ; Retry counter
1111 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1113 .sendreq: push ax ; [bp-2] - Retry counter
1114 push si ; [bp-4] - File name
1116 mov di,packet_buf
1117 mov [pxe_udp_write_pkt.buffer],di
1119 mov ax,TFTP_RRQ ; TFTP opcode
1120 stosw
1122 lodsd ; EAX <- server override (if any)
1123 and eax,eax
1124 jnz .noprefix ; No prefix, and we have the server
1126 push si ; Add common prefix
1127 mov si,PathPrefix
1128 call strcpy
1129 dec di
1130 pop si
1132 mov eax,[ServerIP] ; Get default server
1134 .noprefix:
1135 call strcpy ; Filename
1137 mov [bx+tftp_remoteip],eax
1139 push bx ; [bp-6] - TFTP block
1140 mov bx,[bx]
1141 push bx ; [bp-8] - TID (local port no)
1143 mov [pxe_udp_write_pkt.status],byte 0
1144 mov [pxe_udp_write_pkt.sip],eax
1145 ; Now figure out the gateway
1146 xor eax,[MyIP]
1147 and eax,[Netmask]
1148 jz .nogwneeded
1149 mov eax,[Gateway]
1150 .nogwneeded:
1151 mov [pxe_udp_write_pkt.gip],eax
1152 mov [pxe_udp_write_pkt.lport],bx
1153 mov ax,[ServerPort]
1154 mov [pxe_udp_write_pkt.rport],ax
1155 mov si,tftp_tail
1156 mov cx,tftp_tail_len
1157 rep movsb
1158 sub di,packet_buf ; Get packet size
1159 mov [pxe_udp_write_pkt.buffersize],di
1161 mov di,pxe_udp_write_pkt
1162 mov bx,PXENV_UDP_WRITE
1163 call pxenv
1164 jc .failure
1165 cmp word [pxe_udp_write_pkt.status],byte 0
1166 jne .failure
1169 ; Danger, Will Robinson! We need to support timeout
1170 ; and retry lest we just lost a packet...
1173 ; Packet transmitted OK, now we need to receive
1174 .getpacket: push word [PktTimeout] ; [bp-10]
1175 push word [BIOS_timer] ; [bp-12]
1177 .pkt_loop: mov bx,[bp-8] ; TID
1178 mov di,packet_buf
1179 mov word [pxe_udp_read_pkt.status],0
1180 mov [pxe_udp_read_pkt.buffer],di
1181 mov [pxe_udp_read_pkt.buffer+2],ds
1182 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
1183 mov eax,[MyIP]
1184 mov [pxe_udp_read_pkt.dip],eax
1185 mov [pxe_udp_read_pkt.lport],bx
1186 mov di,pxe_udp_read_pkt
1187 mov bx,PXENV_UDP_READ
1188 call pxenv
1189 and ax,ax
1190 jz .got_packet ; Wait for packet
1191 .no_packet:
1192 mov dx,[BIOS_timer]
1193 cmp dx,[bp-12]
1194 je .pkt_loop
1195 mov [bp-12],dx
1196 dec word [bp-10] ; Timeout
1197 jnz .pkt_loop
1198 pop ax ; Adjust stack
1199 pop ax
1200 shl word [PktTimeout],1 ; Exponential backoff
1201 jmp .failure
1203 .got_packet:
1204 mov si,[bp-6] ; TFTP pointer
1205 mov bx,[bp-8] ; TID
1207 mov eax,[si+tftp_remoteip]
1208 cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
1209 jne .no_packet
1211 ; Got packet - reset timeout
1212 mov word [PktTimeout],PKT_TIMEOUT
1214 pop ax ; Adjust stack
1215 pop ax
1217 mov ax,[pxe_udp_read_pkt.rport]
1218 mov [si+tftp_remoteport],ax
1220 ; filesize <- -1 == unknown
1221 mov dword [si+tftp_filesize], -1
1222 ; Default blksize unless blksize option negotiated
1223 mov word [si+tftp_blksize], TFTP_BLOCKSIZE
1225 mov cx,[pxe_udp_read_pkt.buffersize]
1226 sub cx,2 ; CX <- bytes after opcode
1227 jb .failure ; Garbled reply
1229 mov si,packet_buf
1230 lodsw
1232 cmp ax, TFTP_ERROR
1233 je .bailnow ; ERROR reply: don't try again
1235 cmp ax, TFTP_OACK
1236 jne .no_tsize
1238 ; Now we need to parse the OACK packet to get the transfer
1239 ; size. SI -> first byte of options; CX -> byte count
1240 .parse_oack:
1241 jcxz .no_tsize ; No options acked
1242 .get_opt_name:
1243 mov di,si
1244 mov bx,si
1245 .opt_name_loop: lodsb
1246 and al,al
1247 jz .got_opt_name
1248 or al,20h ; Convert to lowercase
1249 stosb
1250 loop .opt_name_loop
1251 ; We ran out, and no final null
1252 jmp .err_reply
1253 .got_opt_name: ; si -> option value
1254 dec cx ; bytes left in pkt
1255 jz .err_reply ; Option w/o value
1257 ; Parse option pointed to by bx; guaranteed to be
1258 ; null-terminated.
1259 push cx
1260 push si
1261 mov si,bx ; -> option name
1262 mov bx,tftp_opt_table
1263 mov cx,tftp_opts
1264 .opt_loop:
1265 push cx
1266 push si
1267 mov di,[bx] ; Option pointer
1268 mov cx,[bx+2] ; Option len
1269 repe cmpsb
1270 pop si
1271 pop cx
1272 je .get_value ; OK, known option
1273 add bx,6
1274 loop .opt_loop
1276 pop si
1277 pop cx
1278 jmp .err_reply ; Non-negotiated option returned
1280 .get_value: pop si ; si -> option value
1281 pop cx ; cx -> bytes left in pkt
1282 mov bx,[bx+4] ; Pointer to data target
1283 add bx,[bp-6] ; TFTP socket pointer
1284 xor eax,eax
1285 xor edx,edx
1286 .value_loop: lodsb
1287 and al,al
1288 jz .got_value
1289 sub al,'0'
1290 cmp al, 9
1291 ja .err_reply ; Not a decimal digit
1292 imul edx,10
1293 add edx,eax
1294 mov [bx],edx
1295 loop .value_loop
1296 ; Ran out before final null, accept anyway
1297 jmp short .done_pkt
1299 .got_value:
1300 dec cx
1301 jnz .get_opt_name ; Not end of packet
1303 ; ZF == 1
1305 ; Success, done!
1306 .done_pkt:
1307 pop si ; Junk
1308 pop si ; We want the packet ptr in SI
1310 mov eax,[si+tftp_filesize]
1311 cmp eax,-1
1312 jz .no_tsize
1313 mov edx,eax
1314 shr edx,16 ; DX:AX == EAX
1316 and eax,eax ; Set ZF depending on file size
1317 pop bp ; Junk
1318 pop bp ; Junk (retry counter)
1319 jz .error_si ; ZF = 1 need to free the socket
1320 .ret:
1321 pop bp
1322 pop cx
1323 pop bx
1324 pop es
1327 .no_tsize:
1328 .err_reply: ; Option negotiation error. Send ERROR reply.
1329 ; ServerIP and gateway are already programmed in
1330 mov si,[bp-6]
1331 mov ax,[si+tftp_remoteport]
1332 mov word [pxe_udp_write_pkt.rport],ax
1333 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1334 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1335 mov di,pxe_udp_write_pkt
1336 mov bx,PXENV_UDP_WRITE
1337 call pxenv
1339 ; Write an error message and explode
1340 mov si,err_oldtftp
1341 call writestr
1342 jmp kaboom
1344 .bailnow: mov word [bp-2],1 ; Immediate error - no retry
1346 .failure: pop bx ; Junk
1347 pop bx
1348 pop si
1349 pop ax
1350 dec ax ; Retry counter
1351 jnz .sendreq ; Try again
1353 .error: mov si,bx ; Socket pointer
1354 .error_si: ; Socket pointer already in SI
1355 call free_socket ; ZF <- 1, SI <- 0
1356 jmp .ret
1359 ; allocate_socket: Allocate a local UDP port structure
1361 ; If successful:
1362 ; ZF set
1363 ; BX = socket pointer
1364 ; If unsuccessful:
1365 ; ZF clear
1367 allocate_socket:
1368 push cx
1369 mov bx,Files
1370 mov cx,MAX_OPEN
1371 .check: cmp word [bx], byte 0
1372 je .found
1373 add bx,open_file_t_size
1374 loop .check
1375 xor cx,cx ; ZF = 1
1376 pop cx
1378 ; Allocate a socket number. Socket numbers are made
1379 ; guaranteed unique by including the socket slot number
1380 ; (inverted, because we use the loop counter cx); add a
1381 ; counter value to keep the numbers from being likely to
1382 ; get immediately reused.
1384 ; The NextSocket variable also contains the top two bits
1385 ; set. This generates a value in the range 49152 to
1386 ; 57343.
1387 .found:
1388 dec cx
1389 push ax
1390 mov ax,[NextSocket]
1391 inc ax
1392 and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
1393 mov [NextSocket],ax
1394 shl cx,13-MAX_OPEN_LG2
1395 add cx,ax ; ZF = 0
1396 xchg ch,cl ; Convert to network byte order
1397 mov [bx],cx ; Socket in use
1398 pop ax
1399 pop cx
1403 ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
1405 free_socket:
1406 push es
1407 pusha
1408 xor ax,ax
1409 mov es,ax
1410 mov di,si
1411 mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
1412 rep stosw
1413 popa
1414 pop es
1415 xor si,si
1419 ; parse_dotquad:
1420 ; Read a dot-quad pathname in DS:SI and output an IP
1421 ; address in EAX, with SI pointing to the first
1422 ; nonmatching character.
1424 ; Return CF=1 on error.
1426 parse_dotquad:
1427 push cx
1428 mov cx,4
1429 xor eax,eax
1430 .parseloop:
1431 mov ch,ah
1432 mov ah,al
1433 lodsb
1434 sub al,'0'
1435 jb .notnumeric
1436 cmp al,9
1437 ja .notnumeric
1438 aad ; AL += 10 * AH; AH = 0;
1439 xchg ah,ch
1440 jmp .parseloop
1441 .notnumeric:
1442 cmp al,'.'-'0'
1443 pushf
1444 mov al,ah
1445 mov ah,ch
1446 xor ch,ch
1447 ror eax,8
1448 popf
1449 jne .error
1450 loop .parseloop
1451 jmp .done
1452 .error:
1453 loop .realerror ; If CX := 1 then we're done
1455 jmp .done
1456 .realerror:
1458 .done:
1459 dec si ; CF unchanged!
1460 pop cx
1463 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1464 ; to by ES:DI; ends on encountering any whitespace.
1466 ; This verifies that a filename is < FILENAME_MAX characters
1467 ; and doesn't contain whitespace, and zero-pads the output buffer,
1468 ; so "repe cmpsb" can do a compare.
1470 ; The first four bytes of the manged name is the IP address of
1471 ; the download host.
1473 mangle_name:
1474 push si
1475 mov eax,[ServerIP]
1476 cmp byte [si],0
1477 je .noip ; Null filename?!?!
1478 cmp word [si],'::' ; Leading ::?
1479 je .gotprefix
1481 .more:
1482 inc si
1483 cmp byte [si],0
1484 je .noip
1485 cmp word [si],'::'
1486 jne .more
1488 ; We have a :: prefix of some sort, it could be either
1489 ; a DNS name or a dot-quad IP address. Try the dot-quad
1490 ; first...
1491 .here:
1492 pop si
1493 push si
1494 call parse_dotquad
1495 jc .notdq
1496 cmp word [si],'::'
1497 je .gotprefix
1498 .notdq:
1499 pop si
1500 push si
1501 call dns_resolv
1502 cmp word [si],'::'
1503 jne .noip
1504 and eax,eax
1505 jnz .gotprefix
1507 .noip:
1508 pop si
1509 xor eax,eax
1510 jmp .prefix_done
1512 .gotprefix:
1513 pop cx ; Adjust stack
1514 inc si ; Skip double colon
1515 inc si
1517 .prefix_done:
1518 stosd ; Save IP address prefix
1519 mov cx,FILENAME_MAX-5
1521 .mn_loop:
1522 lodsb
1523 cmp al,' ' ; If control or space, end
1524 jna .mn_end
1525 stosb
1526 loop .mn_loop
1527 .mn_end:
1528 inc cx ; At least one null byte
1529 xor ax,ax ; Zero-fill name
1530 rep stosb ; Doesn't do anything if CX=0
1531 ret ; Done
1534 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1535 ; filename to the conventional representation. This is needed
1536 ; for the BOOT_IMAGE= parameter for the kernel.
1537 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1538 ; known to be shorter.
1540 ; DS:SI -> input mangled file name
1541 ; ES:DI -> output buffer
1543 ; On return, DI points to the first byte after the output name,
1544 ; which is set to a null byte.
1546 unmangle_name:
1547 push eax
1548 lodsd
1549 and eax,eax
1550 jz .noip
1551 call gendotquad
1552 mov ax,'::'
1553 stosw
1554 .noip:
1555 call strcpy
1556 dec di ; Point to final null byte
1557 pop eax
1561 ; pxenv
1563 ; This is the main PXENV+/!PXE entry point, using the PXENV+
1564 ; calling convention. This is a separate local routine so
1565 ; we can hook special things from it if necessary. In particular,
1566 ; some PXE stacks seem to not like being invoked from anything but
1567 ; the initial stack, so humour it.
1570 pxenv:
1571 %if USE_PXE_PROVIDED_STACK == 0
1572 mov [cs:PXEStack],sp
1573 mov [cs:PXEStack+2],ss
1574 lss sp,[cs:InitStack]
1575 %endif
1576 .jump: call 0:pxe_thunk ; Default to calling the thunk
1577 %if USE_PXE_PROVIDED_STACK == 0
1578 lss sp,[cs:PXEStack]
1579 %endif
1580 cld ; Make sure DF <- 0
1583 ; Must be after function def due to NASM bug
1584 PXENVEntry equ pxenv.jump+1
1587 ; pxe_thunk
1589 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1590 ; calling convention (using the stack.)
1592 ; This is called as a far routine so that we can just stick it into
1593 ; the PXENVEntry variable.
1595 pxe_thunk: push es
1596 push di
1597 push bx
1598 .jump: call 0:0
1599 add sp,byte 6
1600 cmp ax,byte 1
1601 cmc ; Set CF unless ax == 0
1602 retf
1604 ; Must be after function def due to NASM bug
1605 PXEEntry equ pxe_thunk.jump+1
1608 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1610 ; In this case, get multiple blocks from a specific TCP connection.
1612 ; On entry:
1613 ; ES:BX -> Buffer
1614 ; SI -> TFTP socket pointer
1615 ; CX -> 512-byte block count; 0FFFFh = until end of file
1616 ; On exit:
1617 ; SI -> TFTP socket pointer (or 0 on EOF)
1618 ; CF = 1 -> Hit EOF
1620 getfssec: push si
1621 push fs
1622 mov di,bx
1623 mov bx,si
1624 mov ax,pktbuf_seg
1625 mov fs,ax
1627 movzx ecx,cx
1628 shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
1629 jz .hit_eof ; Nothing to do?
1631 .need_more:
1632 push ecx
1634 movzx eax,word [bx+tftp_bytesleft]
1635 cmp ecx,eax
1636 jna .ok_size
1637 mov ecx,eax
1638 jcxz .need_packet ; No bytes available?
1639 .ok_size:
1641 mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
1642 mov si,[bx+tftp_dataptr]
1643 sub [bx+tftp_bytesleft],cx
1644 fs rep movsb ; Copy from packet buffer
1645 mov [bx+tftp_dataptr],si
1647 pop ecx
1648 sub ecx,eax
1649 jnz .need_more
1652 .hit_eof:
1653 pop fs
1654 pop si
1656 ; Is there anything left of this?
1657 mov eax,[si+tftp_filesize]
1658 sub eax,[si+tftp_filepos]
1659 jnz .bytes_left ; CF <- 0
1661 cmp [si+tftp_bytesleft],ax
1662 jnz .bytes_left ; CF <- 0
1664 ; The socket is closed and the buffer drained
1665 ; Close socket structure and re-init for next user
1666 call free_socket
1668 .bytes_left:
1672 ; No data in buffer, check to see if we can get a packet...
1674 .need_packet:
1675 pop ecx
1676 mov eax,[bx+tftp_filesize]
1677 cmp eax,[bx+tftp_filepos]
1678 je .hit_eof ; Already EOF'd; socket already closed
1680 pushad
1681 push es
1682 mov si,bx
1683 call get_packet
1684 pop es
1685 popad
1687 jmp .need_more
1690 ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
1692 get_packet:
1693 mov ax,ds
1694 mov es,ax
1696 .packet_loop:
1697 ; Start by ACKing the previous packet; this should cause the
1698 ; next packet to be sent.
1699 mov cx,PKT_RETRY
1700 mov word [PktTimeout],PKT_TIMEOUT
1702 .send_ack: push cx ; <D> Retry count
1704 mov ax,[si+tftp_lastpkt]
1705 call ack_packet ; Send ACK
1707 ; We used to test the error code here, but sometimes
1708 ; PXE would return negative status even though we really
1709 ; did send the ACK. Now, just treat a failed send as
1710 ; a normally lost packet, and let it time out in due
1711 ; course of events.
1713 .send_ok: ; Now wait for packet.
1714 mov dx,[BIOS_timer] ; Get current time
1716 mov cx,[PktTimeout]
1717 .wait_data: push cx ; <E> Timeout
1718 push dx ; <F> Old time
1720 mov bx,[si+tftp_pktbuf]
1721 mov [pxe_udp_read_pkt.buffer],bx
1722 mov [pxe_udp_read_pkt.buffer+2],fs
1723 mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
1724 mov eax,[si+tftp_remoteip]
1725 mov [pxe_udp_read_pkt.sip],eax
1726 mov eax,[MyIP]
1727 mov [pxe_udp_read_pkt.dip],eax
1728 mov ax,[si+tftp_remoteport]
1729 mov [pxe_udp_read_pkt.rport],ax
1730 mov ax,[si+tftp_localport]
1731 mov [pxe_udp_read_pkt.lport],ax
1732 mov di,pxe_udp_read_pkt
1733 mov bx,PXENV_UDP_READ
1734 push si ; <G>
1735 call pxenv
1736 pop si ; <G>
1737 and ax,ax
1738 jz .recv_ok
1740 ; No packet, or receive failure
1741 mov dx,[BIOS_timer]
1742 pop ax ; <F> Old time
1743 pop cx ; <E> Timeout
1744 cmp ax,dx ; Same time -> don't advance timeout
1745 je .wait_data ; Same clock tick
1746 loop .wait_data ; Decrease timeout
1748 pop cx ; <D> Didn't get any, send another ACK
1749 shl word [PktTimeout],1 ; Exponential backoff
1750 loop .send_ack
1751 jmp kaboom ; Forget it...
1753 .recv_ok: pop dx ; <F>
1754 pop cx ; <E>
1756 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1757 jb .wait_data ; Bad size for a DATA packet
1759 mov bx,[si+tftp_pktbuf]
1760 cmp word [fs:bx],TFTP_DATA ; Not a data packet?
1761 jne .wait_data ; Then wait for something else
1763 mov ax,[si+tftp_lastpkt]
1764 xchg ah,al ; Host byte order
1765 inc ax ; Which packet are we waiting for?
1766 xchg ah,al ; Network byte order
1767 cmp [fs:bx+2],ax
1768 je .right_packet
1770 ; Wrong packet, ACK the packet and then try again
1771 ; This is presumably because the ACK got lost,
1772 ; so the server just resent the previous packet
1773 mov ax,[fs:bx+2]
1774 call ack_packet
1775 jmp .send_ok ; Reset timeout
1777 .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
1779 pop cx ; <D> Don't need the retry count anymore
1781 mov [si+tftp_lastpkt],ax ; Update last packet number
1783 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1784 sub cx,byte 4 ; Skip TFTP header
1786 ; If this is a zero-length block, don't mess with the pointers,
1787 ; since we may have just set up the previous block that way
1788 jz .last_block
1790 ; Set pointer to data block
1791 lea ax,[bx+4] ; Data past TFTP header
1792 mov [si+tftp_dataptr],ax
1794 add [si+tftp_filepos],ecx
1795 mov [si+tftp_bytesleft],cx
1797 cmp cx,[si+tftp_blksize] ; Is it a full block?
1798 jb .last_block ; If so, it's not EOF
1800 ; If we had the exact right number of bytes, always get
1801 ; one more packet to get the (zero-byte) EOF packet and
1802 ; close the socket.
1803 mov eax,[si+tftp_filepos]
1804 cmp [si+tftp_filesize],eax
1805 je .packet_loop
1810 .last_block: ; Last block - ACK packet immediately
1811 mov ax,[fs:bx+2]
1812 call ack_packet
1814 ; Make sure we know we are at end of file
1815 mov eax,[si+tftp_filepos]
1816 mov [si+tftp_filesize],eax
1821 ; ack_packet:
1823 ; Send ACK packet. This is a common operation and so is worth canning.
1825 ; Entry:
1826 ; SI = TFTP block
1827 ; AX = Packet # to ack (network byte order)
1828 ; Exit:
1829 ; ZF = 0 -> Error
1830 ; All registers preserved
1832 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1834 ack_packet:
1835 pushad
1836 mov [ack_packet_buf+2],ax ; Packet number to ack
1837 mov ax,[si]
1838 mov [pxe_udp_write_pkt.lport],ax
1839 mov ax,[si+tftp_remoteport]
1840 mov [pxe_udp_write_pkt.rport],ax
1841 mov eax,[si+tftp_remoteip]
1842 mov [pxe_udp_write_pkt.sip],eax
1843 xor eax,[MyIP]
1844 and eax,[Netmask]
1845 jz .nogw
1846 mov eax,[Gateway]
1847 .nogw:
1848 mov [pxe_udp_write_pkt.gip],eax
1849 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1850 mov [pxe_udp_write_pkt.buffersize], word 4
1851 mov di,pxe_udp_write_pkt
1852 mov bx,PXENV_UDP_WRITE
1853 call pxenv
1854 cmp ax,byte 0 ; ZF = 1 if write OK
1855 popad
1859 ; unload_pxe:
1861 ; This function unloads the PXE and UNDI stacks and unclaims
1862 ; the memory.
1864 unload_pxe:
1865 test byte [KeepPXE],01h ; Should we keep PXE around?
1866 jnz reset_pxe
1868 push ds
1869 push es
1871 mov ax,cs
1872 mov ds,ax
1873 mov es,ax
1875 mov si,new_api_unload
1876 cmp byte [APIVer+1],2 ; Major API version >= 2?
1877 jae .new_api
1878 mov si,old_api_unload
1879 .new_api:
1881 .call_loop: xor ax,ax
1882 lodsb
1883 and ax,ax
1884 jz .call_done
1885 xchg bx,ax
1886 mov di,pxe_unload_stack_pkt
1887 push di
1888 xor ax,ax
1889 mov cx,pxe_unload_stack_pkt_len >> 1
1890 rep stosw
1891 pop di
1892 call pxenv
1893 jc .cant_free
1894 mov ax,word [pxe_unload_stack_pkt.status]
1895 cmp ax,PXENV_STATUS_SUCCESS
1896 jne .cant_free
1897 jmp .call_loop
1899 .call_done:
1901 ; This isn't necessary anymore; we can use the memory area previously
1902 ; used by the PXE stack indefinitely, and the chainload code sets up
1903 ; a new stack independently. Leave the source code in here for now,
1904 ; but expect to rip it out soonish.
1906 %if 0 ; USE_PXE_PROVIDED_STACK
1907 ; We need to switch to our local stack here...
1908 pusha
1909 pushf
1910 push gs
1912 mov si,sp
1913 mov ax,ss
1914 mov gs,ax
1915 sub ax,[BaseStack+4] ; Are we using the base stack
1916 je .is_base_stack ; (as opposed to the COMBOOT stack)?
1918 lgs si,[SavedSSSP] ; COMBOOT stack
1919 .is_base_stack:
1921 mov cx,[InitStack]
1922 mov di,StackBuf
1923 mov [BaseStack],di
1924 mov [BaseStack+4],es
1925 sub cx,si
1926 sub di,cx
1927 mov [SavedSSSP],di ; New SP
1928 mov [SavedSSSP+2],es
1929 gs rep movsb
1931 and ax,ax ; Remember which stack
1932 jne .combootstack
1934 ; Update the base stack pointer since it's in use
1935 lss sp,[SavedSSSP]
1937 .combootstack:
1938 pop gs
1939 popf
1940 popa
1941 %endif
1942 mov bx,0FF00h
1944 mov dx,[RealBaseMem]
1945 cmp dx,[BIOS_fbm] ; Sanity check
1946 jna .cant_free
1947 inc bx
1949 ; Check that PXE actually unhooked the INT 1Ah chain
1950 movzx eax,word [4*0x1a]
1951 movzx ecx,word [4*0x1a+2]
1952 shl ecx,4
1953 add eax,ecx
1954 shr eax,10
1955 cmp ax,dx ; Not in range
1956 jae .ok
1957 cmp ax,[BIOS_fbm]
1958 jae .cant_free
1959 ; inc bx
1961 .ok:
1962 mov [BIOS_fbm],dx
1963 .pop_ret:
1964 pop es
1965 pop ds
1968 .cant_free:
1969 mov si,cant_free_msg
1970 call writestr
1971 push ax
1972 xchg bx,ax
1973 call writehex4
1974 mov al,'-'
1975 call writechr
1976 pop ax
1977 call writehex4
1978 mov al,'-'
1979 call writechr
1980 mov eax,[4*0x1a]
1981 call writehex8
1982 call crlf
1983 jmp .pop_ret
1985 ; We want to keep PXE around, but still we should reset
1986 ; it to the standard bootup configuration
1987 reset_pxe:
1988 push es
1989 push cs
1990 pop es
1991 mov bx,PXENV_UDP_CLOSE
1992 mov di,pxe_udp_close_pkt
1993 call pxenv
1994 pop es
1998 ; gendotquad
2000 ; Take an IP address (in network byte order) in EAX and
2001 ; output a dotted quad string to ES:DI.
2002 ; DI points to terminal null at end of string on exit.
2004 gendotquad:
2005 push eax
2006 push cx
2007 mov cx,4
2008 .genchar:
2009 push eax
2010 cmp al,10 ; < 10?
2011 jb .lt10 ; If so, skip first 2 digits
2013 cmp al,100 ; < 100
2014 jb .lt100 ; If so, skip first digit
2016 aam 100
2017 ; Now AH = 100-digit; AL = remainder
2018 add ah,'0'
2019 mov [es:di],ah
2020 inc di
2022 .lt100:
2023 aam 10
2024 ; Now AH = 10-digit; AL = remainder
2025 add ah,'0'
2026 mov [es:di],ah
2027 inc di
2029 .lt10:
2030 add al,'0'
2031 stosb
2032 mov al,'.'
2033 stosb
2034 pop eax
2035 ror eax,8 ; Move next char into LSB
2036 loop .genchar
2037 dec di
2038 mov [es:di], byte 0
2039 pop cx
2040 pop eax
2044 ; parse_dhcp
2046 ; Parse a DHCP packet. This includes dealing with "overloaded"
2047 ; option fields (see RFC 2132, section 9.3)
2049 ; This should fill in the following global variables, if the
2050 ; information is present:
2052 ; MyIP - client IP address
2053 ; ServerIP - boot server IP address
2054 ; Netmask - network mask
2055 ; Gateway - default gateway router IP
2056 ; BootFile - boot file name
2057 ; DNSServers - DNS server IPs
2058 ; LocalDomain - Local domain name
2060 ; This assumes the DHCP packet is in "trackbuf" and the length
2061 ; of the packet in in CX on entry.
2064 parse_dhcp:
2065 mov byte [OverLoad],0 ; Assume no overload
2066 mov eax, [trackbuf+bootp.yip]
2067 and eax, eax
2068 jz .noyip
2069 cmp al,224 ; Class D or higher -> bad
2070 jae .noyip
2071 mov [MyIP], eax
2072 .noyip:
2073 mov eax, [trackbuf+bootp.sip]
2074 and eax, eax
2075 jz .nosip
2076 cmp al,224 ; Class D or higher -> bad
2077 jae .nosip
2078 mov [ServerIP], eax
2079 .nosip:
2080 sub cx, bootp.options
2081 jbe .nooptions
2082 mov si, trackbuf+bootp.option_magic
2083 lodsd
2084 cmp eax, BOOTP_OPTION_MAGIC
2085 jne .nooptions
2086 call parse_dhcp_options
2087 .nooptions:
2088 mov si, trackbuf+bootp.bootfile
2089 test byte [OverLoad],1
2090 jz .nofileoverload
2091 mov cx,128
2092 call parse_dhcp_options
2093 jmp short .parsed_file
2094 .nofileoverload:
2095 cmp byte [si], 0
2096 jz .parsed_file ; No bootfile name
2097 mov di,BootFile
2098 mov cx,32
2099 rep movsd
2100 xor al,al
2101 stosb ; Null-terminate
2102 .parsed_file:
2103 mov si, trackbuf+bootp.sname
2104 test byte [OverLoad],2
2105 jz .nosnameoverload
2106 mov cx,64
2107 call parse_dhcp_options
2108 .nosnameoverload:
2112 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2113 ; size is CX -- some DHCP servers leave option fields unterminated
2114 ; in violation of the spec.
2116 ; For parse_some_dhcp_options, DH contains the minimum value for
2117 ; the option to recognize -- this is used to restrict parsing to
2118 ; PXELINUX-specific options only.
2120 parse_dhcp_options:
2121 xor dx,dx
2123 parse_some_dhcp_options:
2124 .loop:
2125 and cx,cx
2126 jz .done
2128 lodsb
2129 dec cx
2130 jz .done ; Last byte; must be PAD, END or malformed
2131 cmp al, 0 ; PAD option
2132 je .loop
2133 cmp al,255 ; END option
2134 je .done
2136 ; Anything else will have a length field
2137 mov dl,al ; DL <- option number
2138 xor ax,ax
2139 lodsb ; AX <- option length
2140 dec cx
2141 sub cx,ax ; Decrement bytes left counter
2142 jb .done ; Malformed option: length > field size
2144 cmp dl,dh ; Is the option value valid?
2145 jb .opt_done
2147 cmp dl,1 ; SUBNET MASK option
2148 jne .not_subnet
2149 mov ebx,[si]
2150 mov [Netmask],ebx
2151 jmp .opt_done
2152 .not_subnet:
2154 cmp dl,3 ; ROUTER option
2155 jne .not_router
2156 mov ebx,[si]
2157 mov [Gateway],ebx
2158 jmp .opt_done
2159 .not_router:
2161 cmp dl,6 ; DNS SERVERS option
2162 jne .not_dns
2163 pusha
2164 mov cx,ax
2165 shr cx,2
2166 cmp cl,DNS_MAX_SERVERS
2167 jna .oklen
2168 mov cl,DNS_MAX_SERVERS
2169 .oklen:
2170 mov di,DNSServers
2171 rep movsd
2172 mov [LastDNSServer],di
2173 popa
2174 jmp .opt_done
2175 .not_dns:
2177 cmp dl,15 ; DNS LOCAL DOMAIN option
2178 jne .not_localdomain
2179 pusha
2180 mov bx,si
2181 add bx,ax
2182 xor ax,ax
2183 xchg [bx],al ; Zero-terminate option
2184 mov di,LocalDomain
2185 call dns_mangle ; Convert to DNS label set
2186 mov [bx],al ; Restore ending byte
2187 popa
2188 jmp .opt_done
2189 .not_localdomain:
2191 cmp dl,43 ; VENDOR ENCAPSULATED option
2192 jne .not_vendor
2193 pusha
2194 mov dh,208 ; Only recognize PXELINUX options
2195 mov cx,ax ; Length of option = max bytes to parse
2196 call parse_some_dhcp_options ; Parse recursive structure
2197 popa
2198 jmp .opt_done
2199 .not_vendor:
2201 cmp dl,52 ; OPTION OVERLOAD option
2202 jne .not_overload
2203 mov bl,[si]
2204 mov [OverLoad],bl
2205 jmp .opt_done
2206 .not_overload:
2208 cmp dl,67 ; BOOTFILE NAME option
2209 jne .not_bootfile
2210 mov di,BootFile
2211 jmp short .copyoption
2212 .done:
2213 ret ; This is here to make short jumps easier
2214 .not_bootfile:
2216 cmp dl,208 ; PXELINUX MAGIC option
2217 jne .not_pl_magic
2218 cmp al,4 ; Must have length == 4
2219 jne .opt_done
2220 cmp dword [si], htonl(0xF100747E) ; Magic number
2221 jne .opt_done
2222 or byte [DHCPMagic], byte 1 ; Found magic #
2223 jmp short .opt_done
2224 .not_pl_magic:
2226 cmp dl,209 ; PXELINUX CONFIGFILE option
2227 jne .not_pl_config
2228 mov di,ConfigName
2229 or byte [DHCPMagic], byte 2 ; Got config file
2230 jmp short .copyoption
2231 .not_pl_config:
2233 cmp dl,210 ; PXELINUX PATHPREFIX option
2234 jne .not_pl_prefix
2235 mov di,PathPrefix
2236 or byte [DHCPMagic], byte 4 ; Got path prefix
2237 jmp short .copyoption
2238 .not_pl_prefix:
2240 cmp dl,211 ; PXELINUX REBOOTTIME option
2241 jne .not_pl_timeout
2242 cmp al,4
2243 jne .opt_done
2244 mov ebx,[si]
2245 xchg bl,bh ; Convert to host byte order
2246 rol ebx,16
2247 xchg bl,bh
2248 mov [RebootTime],ebx
2249 or byte [DHCPMagic], byte 8 ; Got RebootTime
2250 ; jmp short .opt_done
2251 .not_pl_timeout:
2253 ; Unknown option. Skip to the next one.
2254 .opt_done:
2255 add si,ax
2256 .opt_done_noskip:
2257 jmp .loop
2259 ; Common code for copying an option verbatim
2260 .copyoption:
2261 xchg cx,ax
2262 rep movsb
2263 xchg cx,ax ; Now ax == 0
2264 stosb ; Null-terminate
2265 jmp short .opt_done_noskip
2268 ; genipopt
2270 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2271 ; option into IPOption based on a DHCP packet in trackbuf.
2272 ; Assumes CS == DS == ES.
2274 genipopt:
2275 pushad
2276 mov di,IPOption
2277 mov eax,'ip='
2278 stosd
2279 dec di
2280 mov eax,[MyIP]
2281 call gendotquad
2282 mov al,':'
2283 stosb
2284 mov eax,[ServerIP]
2285 call gendotquad
2286 mov al,':'
2287 stosb
2288 mov eax,[Gateway]
2289 call gendotquad
2290 mov al,':'
2291 stosb
2292 mov eax,[Netmask]
2293 call gendotquad ; Zero-terminates its output
2294 sub di,IPOption
2295 mov [IPOptionLen],di
2296 popad
2300 ; Call the receive loop while idle. This is done mostly so we can respond to
2301 ; ARP messages, but perhaps in the future this can be used to do network
2302 ; console.
2304 ; hpa sez: people using automatic control on the serial port get very
2305 ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
2306 ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
2307 ; passed since the last poll, and reset this when a character is
2308 ; received (RESET_IDLE).
2310 reset_idle:
2311 push ax
2312 mov ax,[cs:BIOS_timer]
2313 mov [cs:IdleTimer],ax
2314 pop ax
2317 check_for_arp:
2318 push ax
2319 mov ax,[cs:BIOS_timer]
2320 sub ax,[cs:IdleTimer]
2321 cmp ax,4
2322 pop ax
2323 jae .need_poll
2325 .need_poll: pushad
2326 push ds
2327 push es
2328 mov ax,cs
2329 mov ds,ax
2330 mov es,ax
2331 mov di,packet_buf
2332 mov [pxe_udp_read_pkt.status],al ; 0
2333 mov [pxe_udp_read_pkt.buffer],di
2334 mov [pxe_udp_read_pkt.buffer+2],ds
2335 mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
2336 mov eax,[MyIP]
2337 mov [pxe_udp_read_pkt.dip],eax
2338 mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
2339 mov di,pxe_udp_read_pkt
2340 mov bx,PXENV_UDP_READ
2341 call pxenv
2342 ; Ignore result...
2343 pop es
2344 pop ds
2345 popad
2346 RESET_IDLE
2349 ; -----------------------------------------------------------------------------
2350 ; Common modules
2351 ; -----------------------------------------------------------------------------
2353 %include "getc.inc" ; getc et al
2354 %include "conio.inc" ; Console I/O
2355 %include "writestr.inc" ; String output
2356 writestr equ cwritestr
2357 %include "writehex.inc" ; Hexadecimal output
2358 %include "parseconfig.inc" ; High-level config file handling
2359 %include "parsecmd.inc" ; Low-level config file handling
2360 %include "bcopy32.inc" ; 32-bit bcopy
2361 %include "loadhigh.inc" ; Load a file into high memory
2362 %include "font.inc" ; VGA font stuff
2363 %include "graphics.inc" ; VGA graphics
2364 %include "highmem.inc" ; High memory sizing
2365 %include "strcpy.inc" ; strcpy()
2366 %include "rawcon.inc" ; Console I/O w/o using the console functions
2367 %include "dnsresolv.inc" ; DNS resolver
2369 ; -----------------------------------------------------------------------------
2370 ; Begin data section
2371 ; -----------------------------------------------------------------------------
2373 section .data
2375 hextbl_lower db '0123456789abcdef'
2376 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2377 db CR, LF, 0
2378 boot_prompt db 'boot: ', 0
2379 wipe_char db BS, ' ', BS, 0
2380 err_notfound db 'Could not find kernel image: ',0
2381 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
2382 err_noram db 'It appears your computer has less than '
2383 asciidec dosram_k
2384 db 'K of low ("DOS")'
2385 db CR, LF
2386 db 'RAM. Linux needs at least this amount to boot. If you get'
2387 db CR, LF
2388 db 'this message in error, hold down the Ctrl key while'
2389 db CR, LF
2390 db 'booting, and I will take your word for it.', CR, LF, 0
2391 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
2392 err_noparm db 'Missing parameter in config file.', CR, LF, 0
2393 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
2394 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
2395 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
2396 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
2397 db CR, LF, 0
2398 err_notdos db ': attempted DOS system call', CR, LF, 0
2399 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
2400 err_bssimage db 'BSS images not supported.', CR, LF, 0
2401 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
2402 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2403 bailmsg equ err_bootfailed
2404 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2405 err_pxefailed db 'PXE API call failed, error ', 0
2406 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2407 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2408 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2409 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2410 apiver_str db 'PXE API version is ',0
2411 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2412 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2413 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2414 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2415 undi_data_msg db 'UNDI data segment at: ',0
2416 undi_data_len_msg db 'UNDI data segment size: ',0
2417 undi_code_msg db 'UNDI code segment at: ',0
2418 undi_code_len_msg db 'UNDI code segment size: ',0
2419 cant_free_msg db 'Failed to free base memory, error ', 0
2420 notfound_msg db 'not found', CR, LF, 0
2421 myipaddr_msg db 'My IP address seems to be ',0
2422 tftpprefix_msg db 'TFTP prefix: ', 0
2423 localboot_msg db 'Booting from local disk...', CR, LF, 0
2424 cmdline_msg db 'Command line: ', CR, LF, 0
2425 ready_msg db 'Ready.', CR, LF, 0
2426 trying_msg db 'Trying to load: ', 0
2427 crlfloading_msg db CR, LF ; Fall through
2428 loading_msg db 'Loading ', 0
2429 dotdot_msg db '.'
2430 dot_msg db '.', 0
2431 fourbs_msg db BS, BS, BS, BS, 0
2432 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
2433 crlf_msg db CR, LF
2434 null_msg db 0
2435 crff_msg db CR, FF, 0
2436 default_str db 'default', 0
2437 default_len equ ($-default_str)
2438 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2439 cfgprefix db 'pxelinux.cfg/' ; No final null!
2440 cfgprefix_len equ ($-cfgprefix)
2443 ; Command line options we'd like to take a look at
2445 ; mem= and vga= are handled as normal 32-bit integer values
2446 initrd_cmd db 'initrd='
2447 initrd_cmd_len equ $-initrd_cmd
2449 ; This one we make ourselves
2450 bootif_str db 'BOOTIF='
2451 bootif_str_len equ $-bootif_str
2453 ; Config file keyword table
2455 %include "keywords.inc"
2458 ; Extensions to search for (in *forward* order).
2459 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2461 align 4, db 0
2462 exten_table: db '.cbt' ; COMBOOT (specific)
2463 db '.0', 0, 0 ; PXE bootstrap program
2464 db '.com' ; COMBOOT (same as DOS)
2465 db '.c32' ; COM32
2466 exten_table_end:
2467 dd 0, 0 ; Need 8 null bytes here
2470 ; PXE unload sequences
2472 new_api_unload:
2473 db PXENV_UDP_CLOSE
2474 db PXENV_UNDI_SHUTDOWN
2475 db PXENV_UNLOAD_STACK
2476 db PXENV_STOP_UNDI
2477 db 0
2478 old_api_unload:
2479 db PXENV_UDP_CLOSE
2480 db PXENV_UNDI_SHUTDOWN
2481 db PXENV_UNLOAD_STACK
2482 db PXENV_UNDI_CLEANUP
2483 db 0
2486 ; PXE query packets partially filled in
2488 pxe_bootp_query_pkt_2:
2489 .status: dw 0 ; Status
2490 .packettype: dw 2 ; DHCPACK packet
2491 .buffersize: dw trackbufsize ; Packet size
2492 .buffer: dw trackbuf, 0 ; seg:off of buffer
2493 .bufferlimit: dw trackbufsize ; Unused
2495 pxe_bootp_query_pkt_3:
2496 .status: dw 0 ; Status
2497 .packettype: dw 3 ; Boot server packet
2498 .buffersize: dw trackbufsize ; Packet size
2499 .buffer: dw trackbuf, 0 ; seg:off of buffer
2500 .bufferlimit: dw trackbufsize ; Unused
2502 pxe_bootp_size_query_pkt:
2503 .status: dw 0 ; Status
2504 .packettype: dw 2 ; DHCPACK packet
2505 .buffersize: dw 0 ; Packet size
2506 .buffer: dw 0, 0 ; seg:off of buffer
2507 .bufferlimit: dw 0 ; Unused
2509 pxe_udp_open_pkt:
2510 .status: dw 0 ; Status
2511 .sip: dd 0 ; Source (our) IP
2513 pxe_udp_close_pkt:
2514 .status: dw 0 ; Status
2516 pxe_udp_write_pkt:
2517 .status: dw 0 ; Status
2518 .sip: dd 0 ; Server IP
2519 .gip: dd 0 ; Gateway IP
2520 .lport: dw 0 ; Local port
2521 .rport: dw 0 ; Remote port
2522 .buffersize: dw 0 ; Size of packet
2523 .buffer: dw 0, 0 ; seg:off of buffer
2525 pxe_udp_read_pkt:
2526 .status: dw 0 ; Status
2527 .sip: dd 0 ; Source IP
2528 .dip: dd 0 ; Destination (our) IP
2529 .rport: dw 0 ; Remote port
2530 .lport: dw 0 ; Local port
2531 .buffersize: dw 0 ; Max packet size
2532 .buffer: dw 0, 0 ; seg:off of buffer
2535 ; Misc initialized (data) variables
2537 alignb 4, db 0
2538 BaseStack dd StackBuf ; ESP of base stack
2539 dw 0 ; SS of base stack
2540 NextSocket dw 49152 ; Counter for allocating socket numbers
2541 KeepPXE db 0 ; Should PXE be kept around?
2544 ; TFTP commands
2546 tftp_tail db 'octet', 0 ; Octet mode
2547 tsize_str db 'tsize' ,0 ; Request size
2548 tsize_len equ ($-tsize_str)
2549 db '0', 0
2550 blksize_str db 'blksize', 0 ; Request large blocks
2551 blksize_len equ ($-blksize_str)
2552 asciidec TFTP_LARGEBLK
2553 db 0
2554 tftp_tail_len equ ($-tftp_tail)
2556 alignb 2, db 0
2558 ; Options negotiation parsing table (string pointer, string len, offset
2559 ; into socket structure)
2561 tftp_opt_table:
2562 dw tsize_str, tsize_len, tftp_filesize
2563 dw blksize_str, blksize_len, tftp_blksize
2564 tftp_opts equ ($-tftp_opt_table)/6
2567 ; Error packet to return on options negotiation error
2569 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2570 dw TFTP_EOPTNEG ; ERROR 8: bad options
2571 db 'tsize option required', 0 ; Error message
2572 tftp_opt_err_len equ ($-tftp_opt_err)
2574 alignb 4, db 0
2575 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2578 ; IP information (initialized to "unknown" values)
2579 MyIP dd 0 ; My IP address
2580 ServerIP dd 0 ; IP address of boot server
2581 Netmask dd 0 ; Netmask of this subnet
2582 Gateway dd 0 ; Default router
2583 ServerPort dw TFTP_PORT ; TFTP server port
2586 ; Variables that are uninitialized in SYSLINUX but initialized here
2588 alignb 4, db 0
2589 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2590 BufSafeSec dw trackbufsize/512 ; = how many sectors?
2591 BufSafeBytes dw trackbufsize ; = how many bytes?
2592 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
2593 %ifndef DEPEND
2594 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2595 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2596 %endif
2597 %endif
2598 IPAppend db 0 ; Default IPAPPEND option
2599 DHCPMagic db 0 ; DHCP site-specific option info