1 ; -*- fundamental -*- (asm-mode sucks)
3 ; ****************************************************************************
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
11 ; Copyright (C) 1994-2002 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., 675 Mass Ave, Cambridge MA 02139,
16 ; USA; either version 2 of the License, or (at your option) any later
17 ; version; incorporated herein by reference.
19 ; ****************************************************************************
26 %include "tracers.inc"
30 ; Some semi-configurable constants... change on your own risk.
33 FILENAME_MAX_LG2 equ 6 ; log2(Max filename size Including final null)
34 FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
35 NULLFILE equ 0 ; Zero byte == null file name
36 REBOOT_TIME equ 5*60 ; If failure, time until full reset
37 %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
38 MAX_SOCKETS_LG2 equ 6 ; log2(Max number of open sockets)
39 MAX_SOCKETS equ (1 << MAX_SOCKETS_LG2)
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 TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
44 TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
47 ; TFTP operation codes
49 TFTP_RRQ equ htons(1) ; Read request
50 TFTP_WRQ equ htons(2) ; Write request
51 TFTP_DATA equ htons(3) ; Data packet
52 TFTP_ACK equ htons(4) ; ACK packet
53 TFTP_ERROR equ htons(5) ; ERROR packet
54 TFTP_OACK equ htons(6) ; OACK packet
57 ; Should be updated with every release to avoid bootsector/SYS file mismatch
59 %define version_str VERSION ; Must be 4 characters long!
60 %define date DATE_STR ; Defined from the Makefile
64 ; The following structure is used for "virtual kernels"; i.e. LILO-style
65 ; option labels. The options we permit here are `kernel' and `append
66 ; Since there is no room in the bottom 64K for all of these, we
67 ; stick them at vk_seg:0000 and copy them down before we need them.
69 ; Note: this structure can be added to, but it must
71 %define vk_power 7 ; log2(max number of vkernels)
72 %define max_vk (1 << vk_power) ; Maximum number of vkernels
73 %define vk_shift (16-vk_power) ; Number of bits to shift
74 %define vk_size (1 << vk_shift) ; Size of a vkernel buffer
77 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
78 vk_rname: resb FILENAME_MAX ; Real name
79 vk_ipappend: resb 1 ; "IPAPPEND" flag
83 vk_append: resb max_cmd_len+1 ; Command line
85 vk_end: equ $ ; Should be <= vk_size
89 %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
90 %error "Too many vkernels defined, reduce vk_power"
95 ; Segment assignments in the bottom 640K
96 ; 0000h - main code/data segment (and BIOS segment)
98 real_mode_seg equ 5000h
99 vk_seg equ 4000h ; Virtual kernels
100 xfer_buf_seg equ 3000h ; Bounce buffer for I/O to high mem
101 comboot_seg equ 2000h ; COMBOOT image loading zone
104 ; BOOTP/DHCP packet pattern
108 .opcode resb 1 ; BOOTP/DHCP "opcode"
109 .hardware resb 1 ; ARP hardware type
110 .hardlen resb 1 ; Hardware address length
111 .gatehops resb 1 ; Used by forwarders
112 .ident resd 1 ; Transaction ID
113 .seconds resw 1 ; Seconds elapsed
114 .flags resw 1 ; Broadcast flags
115 .cip resd 1 ; Client IP
116 .yip resd 1 ; "Your" IP
117 .sip resd 1 ; Next server IP
118 .gip resd 1 ; Relay agent IP
119 .macaddr resb 16 ; Client MAC address
120 .sname resb 64 ; Server name (optional)
121 .bootfile resb 128 ; Boot file name
122 .option_magic resd 1 ; Vendor option magic cookie
123 .options resb 1260 ; Vendor options
126 BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
129 ; TFTP connection data structure. Each one of these corresponds to a local
130 ; UDP port. The size of this structure must be a power of 2.
133 tftp_localport resw 1 ; Local port number (0 = not in use)
134 tftp_remoteport resw 1 ; Remote port number
135 tftp_remoteip resd 1 ; Remote IP address
136 tftp_filepos resd 1 ; Position within file
137 tftp_filesize resd 1 ; Total file size
141 %if (tftp_port_t_size & (tftp_port_t_size-1))
142 %error "tftp_port_t is not a power of 2"
146 ; ---------------------------------------------------------------------------
148 ; ---------------------------------------------------------------------------
151 ; Memory below this point is reserved for the BIOS and the MBR
154 trackbuf resb 8192 ; Track buffer goes here
155 trackbufsize equ $-trackbuf
156 ; trackbuf ends at 3000h
160 ; Constants for the xfer_buf_seg
162 ; The xfer_buf_seg is also used to store message file buffers. We
163 ; need two trackbuffers (text and graphics), plus a work buffer
164 ; for the graphics decompressor.
166 xbs_textbuf equ 0 ; Also hard-coded, do not change
167 xbs_vgabuf equ trackbufsize
168 xbs_vgatmpbuf equ 2*trackbufsize
170 absolute 5000h ; Here we keep our BSS stuff
171 VKernelBuf: resb vk_size ; "Current" vkernel
173 AppendBuf resb max_cmd_len+1 ; append=
174 KbdMap resb 256 ; Keyboard map
175 BootFile resb 256 ; Boot file from DHCP packet
176 PathPrefix resb 256 ; Path prefix derived from the above
177 ConfigName resb 256 ; Configuration file from DHCP option
178 FKeyName resb 10*FILENAME_MAX ; File names for F-key help
179 NumBuf resb 15 ; Buffer to load number
180 NumBufEnd resb 1 ; Last byte in NumBuf
181 DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
182 IPOption resb 80 ; ip= option buffer
184 KernelName resb FILENAME_MAX ; Mangled name for kernel
185 KernelCName resb FILENAME_MAX ; Unmangled kernel name
186 InitRDCName resb FILENAME_MAX ; Unmangled initrd name
187 MNameBuf resb FILENAME_MAX
188 InitRD resb FILENAME_MAX
189 PartInfo resb 16 ; Partition table entry
190 E820Buf resd 5 ; INT 15:E820 data buffer
191 HiLoadAddr resd 1 ; Address pointer for high load loop
192 HighMemSize resd 1 ; End of memory pointer (bytes)
193 RamdiskMax resd 1 ; Highest address for a ramdisk
194 KernelSize resd 1 ; Size of kernel (bytes)
195 SavedSSSP resd 1 ; Our SS:SP while running a COMBOOT image
196 PMESP resd 1 ; Protected-mode ESP
197 Stack resd 1 ; Pointer to reset stack
198 PXEEntry resd 1 ; !PXE API entry point
199 RebootTime resd 1 ; Reboot timeout, if set by option
200 KernelClust resd 1 ; Kernel size in clusters
201 StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
202 FBytes equ $ ; Used by open/getc
205 FClust resw 1 ; Number of clusters in open/getc file
206 FNextClust resw 1 ; Pointer to next cluster in d:o
207 FPtr resw 1 ; Pointer to next char in buffer
208 CmdOptPtr resw 1 ; Pointer to first option on cmd line
209 KernelCNameLen resw 1 ; Length of unmangled kernel name
210 InitRDCNameLen resw 1 ; Length of unmangled initrd name
211 NextCharJump resw 1 ; Routine to interpret next print char
212 SetupSecs resw 1 ; Number of setup sectors
213 A20Test resw 1 ; Counter for testing status of A20
214 A20Type resw 1 ; A20 type
215 CmdLineLen resw 1 ; Length of command line including null
216 GraphXSize resw 1 ; Width of splash screen file
217 VGAPos resw 1 ; Pointer into VGA memory
218 VGACluster resw 1 ; Cluster pointer for VGA image file
219 VGAFilePtr resw 1 ; Pointer into VGAFileBuf
220 ConfigFile resw 1 ; Socket for config file
221 PktTimeout resw 1 ; Timeout for current packet
222 KernelExtPtr resw 1 ; During search, final null pointer
223 IPOptionLen resw 1 ; Length of IPOption
224 LocalBootType resw 1 ; Local boot return code
225 RealBaseMem resw 1 ; Amount of DOS memory after freeing
226 APIVer resw 1 ; PXE API version found
228 TextAttribute resb 1 ; Text attribute for message file
229 TextPage resb 1 ; Active display page
231 CursorCol resb 1 ; Cursor column for message file
232 CursorRow resb 1 ; Cursor row for message file
234 VidCols resb 1 ; Columns on screen-1
235 VidRows resb 1 ; Rows on screen-1
236 BaudDivisor resw 1 ; Baud rate divisor
238 FlowOutput resb 1 ; Outputs to assert for serial flow
239 FlowInput resb 1 ; Input bits for serial flow
240 FlowIgnore resb 1 ; Ignore input unless these bits set
241 RetryCount resb 1 ; Used for disk access retries
242 KbdFlags resb 1 ; Check for keyboard escapes
243 LoadFlags resb 1 ; Loadflags from kernel
244 A20Tries resb 1 ; Times until giving up on A20
245 FuncFlag resb 1 ; == 1 if <Ctrl-F> pressed
246 DisplayMask resb 1 ; Display modes mask
247 OverLoad resb 1 ; Set if DHCP packet uses "overloading"
248 TextColorReg resb 17 ; VGA color registers for text mode
249 VGAFileBuf resb FILENAME_MAX ; Unmangled VGA image name
251 VGAFileMBuf resb FILENAME_MAX ; Mangled VGA image name
254 ; PXE packets which don't need static initialization
257 pxe_unload_stack_pkt:
258 .status: resw 1 ; Status
259 .reserved: resw 10 ; Reserved
260 pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
262 alignb tftp_port_t_size
263 Sockets resb MAX_SOCKETS*tftp_port_t_size
266 ; BOOTP/DHCP packet buffer
269 packet_buf resb 2048 ; Transfer packet
270 packet_buf_size equ $-packet_buf
275 ; Primary entry point.
279 jmp 0:_start1 ; Canonicalize address
281 pushad ; Paranoia... in case of return to PXE
282 pushfd ; ... save as much state as possible
289 les bx,[bp+48] ; Initial !PXE structure pointer
293 sti ; Stack already set up by PXE
300 ; Initialize screen (if we're using one)
302 ; Now set up screen parameters
305 ; Wipe the F-key area
308 mov cx,10*(1 << FILENAME_MAX_LG2)
309 push es ; Save ES -> PXE structure
316 ; Tell the user we got this far
318 mov si,syslinux_banner
325 ; Assume API version 2.1, in case we find the !PXE structure without
326 ; finding the PXENV+ structure. This should really look at the Base
327 ; Code ROM ID structure in have_pxe, but this is adequate for now --
328 ; if we have !PXE, we have to be 2.1 or higher, and we don't care
329 ; about higher versions than that.
331 mov word [APIVer],0201h
334 ; Now we need to find the !PXE structure. It's *supposed* to be pointed
335 ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
337 cmp dword [es:bx], '!PXE'
340 ; Uh-oh, not there... try plan B
347 ; Okay, that gave us the PXENV+ structure, find !PXE
348 ; structure from that (if available)
349 cmp dword [es:bx], 'PXEN'
351 cmp word [es:bx+4], 'V+'
354 ; Nothing there either. Last-ditch: scan memory
355 call memory_scan_for_pxe_struct ; !PXE scan
357 call memory_scan_for_pxenv_struct ; PXENV+ scan
360 no_pxe: mov si,err_nopxe
378 cmp ax,0201h ; API version 2.1 or higher
382 les bx,[es:bx+28h] ; !PXE structure pointer
383 cmp dword [es:bx],'!PXE'
386 ; Nope, !PXE structure missing despite API 2.1+, or at least
387 ; the pointer is missing. Do a last-ditch attempt to find it.
388 call memory_scan_for_pxe_struct
391 ; Otherwise, no dice, use PXENV+ structure
395 old_api: ; Need to use a PXENV+ structure
396 mov si,using_pxenv_msg
399 mov eax,[es:bx+0Ah] ; PXE RM API
402 mov si,undi_data_msg ; ***
407 mov si,undi_data_len_msg
417 mov si,undi_code_len_msg
423 ; Compute base memory size from PXENV+ structure
425 movzx eax,word [es:bx+20h] ; UNDI data seg
426 cmp ax,[es:bx+24h] ; UNDI code seg
436 shr eax,10 ; Convert to kilobytes
439 mov si,pxenventry_msg
441 mov ax,[PXENVEntry+2]
457 mov si,undi_data_msg ; ***
462 mov si,undi_data_len_msg
472 mov si,undi_code_len_msg
478 ; Compute base memory size from !PXE structure
506 ; Clear Sockets structures
509 mov cx,(MAX_SOCKETS*tftp_port_t_size)/4
511 push es ; Save ES -> PXE structure
518 ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
519 ; address). This lives in the DHCPACK packet (query info 2).
524 mov di,pxe_bootp_query_pkt_2
525 mov bx,PXENV_GET_CACHED_INFO
527 call far [PXENVEntry]
528 push word [pxe_bootp_query_pkt_2.status]
533 mov di,pxe_bootp_size_query_pkt
534 mov bx,PXENV_GET_CACHED_INFO
536 call far [PXENVEntry]
539 mov ax,[pxe_bootp_size_query_pkt.buffersize]
552 jmp kaboom ; We're dead
555 pop cx ; Forget status
556 mov cx,[pxe_bootp_query_pkt_2.buffersize]
557 call parse_dhcp ; Parse DHCP packet
560 ; Now, get the boot file and other info. This lives in the CACHED_REPLY
561 ; packet (query info 3).
563 mov [pxe_bootp_size_query_pkt.packettype], byte 3
565 mov di,pxe_bootp_query_pkt_3
566 mov bx,PXENV_GET_CACHED_INFO
568 call far [PXENVEntry]
569 push word [pxe_bootp_query_pkt_3.status]
574 ; Packet loaded OK...
575 pop cx ; Forget status
576 mov cx,[pxe_bootp_query_pkt_3.buffersize]
577 call parse_dhcp ; Parse DHCP packet
579 ; Generate ip= option
589 call gendotquad ; This takes network byte order input
591 xchg ah,al ; Convert to host byte order
592 ror eax,16 ; (BSWAP doesn't work on 386)
604 mov si,IPOption ; ***
609 ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
610 ; if we didn't get the magic enable, do not recognize any other options.
613 test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
615 mov byte [DHCPMagic], 0 ; If not, kill all other options
620 ; Initialize UDP stack
624 mov [pxe_udp_open_pkt.sip],eax
625 mov di,pxe_udp_open_pkt
626 mov bx,PXENV_UDP_OPEN
627 call far [PXENVEntry]
629 cmp word [pxe_udp_open_pkt.status], byte 0
631 .failed: mov si,err_udpinit
637 ; Common initialization code
639 %include "cpuinit.inc"
642 ; Now we're all set to start with our *real* business. First load the
643 ; configuration file (if any) and parse it.
645 ; In previous versions I avoided using 32-bit registers because of a
646 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
647 ; random. I figure, though, that if there are any of those still left
648 ; they probably won't be trying to install Linux on them...
650 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
651 ; to take'm out. In fact, we may want to put them back if we're going
652 ; to boot ELKS at some point.
654 mov si,linuxauto_cmd ; Default command: "linux auto"
659 mov di,KbdMap ; Default keymap 1:1
668 ; Store standard filename prefix
670 prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
676 lea cx,[di-PathPrefix-1]
678 lea si,[di-2] ; Skip final null!
681 cmp al,'.' ; Count . or - as alphanum
693 .alnum: loop .find_alnum
695 .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
698 mov si,tftpprefix_msg
705 ; Load configuration file
707 find_config: mov di,trackbuf
713 xchg ah,al ; Convert to host byte order
716 .hexify_loop: rol eax,4
729 ; Begin looking for configuration file
732 test byte [DHCPMagic], 02h
735 ; We got a DHCP option, try it first
747 .no_option: ; Have to guess config file name
749 mov cx,9 ; Up to 9 attempts
751 .tryagain: mov byte [di],0
757 rep movsb ; Copy "default" string
776 ; Now we have the config file open
778 .success: add sp,byte 16 ; Adjust stack
779 call parse_config ; Parse configuration file
782 ; Check whether or not we are supposed to display the boot prompt.
785 cmp word [ForcePrompt],byte 0 ; Force prompt?
787 test byte [KbdFlags],5Bh ; Caps, Scroll, Shift, Alt
788 jz auto_boot ; If neither, default boot
794 mov byte [FuncFlag],0 ; <Ctrl-F> not pressed
797 ; get the very first character -- we can either time
798 ; out, or receive a character press at this time. Some dorky BIOSes stuff
799 ; a return in the buffer on bootup, so wipe the keyboard buffer first.
801 clear_buffer: mov ah,1 ; Check for pending char
806 jmp short clear_buffer
811 jz get_char ; Timeout == 0 -> no timeout
812 inc cx ; The first loop will happen
813 ; immediately as we don't
814 ; know the appropriate DX value
819 mov dx,[BIOS_timer] ; Get time "of day"
821 cmp dx,ax ; Has the timer advanced?
824 loop time_loop ; If so, decrement counter
826 jmp command_done ; Timeout!
828 get_char_pop: pop eax ; Clear stack
836 got_ascii: cmp al,7Fh ; <DEL> == <BS>
841 cmp di,command_line ; Space must not be first
843 enter_char: test byte [FuncFlag],1
845 mov byte [FuncFlag],0
851 .not_ctrl_f: cmp di,max_cmd_len+command_line ; Check there's space
854 call writechr ; Echo to screen
855 get_char_2: jmp short get_char
856 not_ascii: mov byte [FuncFlag],0
859 cmp al,06h ; <Ctrl-F>
861 cmp al,08h ; Backspace
863 backspace: cmp di,command_line ; Make sure there is anything
864 je get_char ; to erase
865 dec di ; Unstore one character
866 mov si,wipe_char ; and erase it from the screen
871 mov byte [FuncFlag],1
874 ctrl_f_0: add al,10 ; <Ctrl-F>0 == F10
881 ; AL = 0 if we get here
888 show_help: ; AX = func key # (0 = F1, 9 = F10)
889 shl ax,FILENAME_MAX_LG2 ; Convert to pointer
892 cmp byte [di],NULLFILE
893 je get_char_2 ; Undefined F-key
906 pop di ; Command line write pointer
908 mov byte [di],0 ; Null-terminate command line
910 call cwritestr ; Write command line so far
916 mov cx,(max_cmd_len+4) >> 2
918 jmp short load_kernel
921 cmp di,command_line ; Did we just hit return?
923 xor al,al ; Store a final null
926 load_kernel: ; Load the kernel now
928 ; First we need to mangle the kernel name the way DOS would...
938 ; Fast-forward to first option (we start over from the beginning, since
939 ; mangle_name doesn't necessarily return a consistent ending state.)
944 clin_is_wsp: and al,al
949 clin_opt_ptr: dec si ; Point to first nonblank
950 mov [CmdOptPtr],si ; Save ptr to first option
952 ; Now check if it is a "virtual kernel"
960 xor si,si ; Point to first vkernel
963 repe cmpsb ; Is this it?
970 ; Not a "virtual kernel" - check that's OK and construct the command line
972 cmp word [AllowImplicit],byte 0
988 ; Find the kernel on disk
990 get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/extension
993 mov cx,FILENAME_MAX-5 ; Need 4 chars + null
994 repne scasb ; Scan for final null
996 dec di ; Point to final null
997 .no_skip: mov [KernelExtPtr],di
999 .search_loop: push bx
1000 mov di,KernelName ; Search on disk
1004 mov eax,[bx] ; Try a different extension
1005 mov si,[KernelExtPtr]
1009 cmp bx,exten_table_end
1010 jna .search_loop ; allow == case (final case)
1015 call unmangle_name ; Get human form
1016 mov si,err_notfound ; Complain about missing kernel
1018 pop si ; KernelCName
1021 jmp abort_load ; Ask user for clue
1023 ; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
1025 bad_implicit: mov si,KernelName ; For the error message
1028 jmp short bad_kernel
1030 ; vk_found: We *are* using a "virtual kernel"
1037 push es ; Restore old DS
1040 push word real_mode_seg
1042 mov di,cmd_line_here
1043 mov si,VKernelBuf+vk_append
1044 mov cx,[VKernelBuf+vk_appendlen]
1046 mov [CmdLinePtr],di ; Where to add rest of cmd
1048 pop di ; DI -> KernelName
1050 mov si,VKernelBuf+vk_rname
1051 mov cx,FILENAME_MAX ; We need ECX == CX later
1054 mov al,[VKernelBuf+vk_ipappend]
1056 xor bx,bx ; Try only one version
1058 ; Is this a "localboot" pseudo-kernel?
1059 cmp byte [VKernelBuf+vk_rname], 0
1060 jne get_kernel ; No, it's real, go get it
1062 mov ax, [VKernelBuf+vk_rname+1]
1065 ; kernel_corrupt: Called if the kernel file does not seem healthy
1067 kernel_corrupt: mov si,err_notkernel
1070 ; This is it! We have a name (and location on the disk)... let's load
1071 ; that sucker!! First we have to decide what kind of file this is; base
1072 ; that decision on the file extension. The following extensions are
1073 ; recognized; case insensitive:
1075 ; .com - COMBOOT image
1076 ; .cbt - COMBOOT image
1077 ; .c32 - COM32 image
1079 ; .0 - PXE bootstrap program (PXELINUX only)
1080 ; .bin - Boot sector
1081 ; .bss - Boot sector, but transfer over DOS superblock (SYSLINUX only)
1082 ; .img - Floppy image (ISOLINUX only)
1084 ; Boot sectors are currently not supported by PXELINUX.
1086 ; Anything else is assumed to be a Linux kernel.
1094 mov [KernelCNameLen],di
1105 .one_step: mov ecx,[di-4] ; 4 bytes before end
1110 ; At this point, DX:AX contains the size of the kernel, and SI contains
1111 ; the file handle/cluster pointer.
1113 or ecx,20202000h ; Force lower case
1131 ; Otherwise Linux kernel
1133 ; Linux kernel loading code is common. However, we need to define
1134 ; a couple of helper macros...
1137 ; Handle "ipappend" option
1138 %define HAVE_SPECIAL_APPEND
1139 %macro SPECIAL_APPEND 0
1140 mov al,[IPAppend] ; ip=
1144 mov cx,[IPOptionLen]
1152 %define HAVE_UNLOAD_PREP
1153 %macro UNLOAD_PREP 0
1158 mov sp,7C00h ; Set up a conventional stack
1161 %include "runkernel.inc"
1164 ; COMBOOT-loading code
1166 %include "comboot.inc"
1167 %include "com32.inc"
1170 ; Boot sector loading code
1172 %include "bootsect.inc"
1175 ; Boot to the local disk by returning the appropriate PXE magic.
1176 ; AX contains the appropriate return code.
1180 lss sp,[cs:Stack] ; Restore stack pointer
1182 mov [LocalBootType],ax
1183 mov si,localboot_msg
1185 ; Restore the environment we were called with
1192 mov ax,[cs:LocalBootType]
1193 retf ; Return to PXE
1196 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1207 ac_kill: mov si,aborted_msg
1210 ; abort_load: Called by various routines which wants to print a fatal
1211 ; error message and return to the command prompt. Since this
1212 ; may happen at just about any stage of the boot process, assume
1213 ; our state is messed up, and just reset the segment registers
1214 ; and the stack forcibly.
1216 ; SI = offset (in _text) of error message to print
1219 mov ax,cs ; Restore CS = DS = ES
1223 lss sp,[cs:Stack] ; Reset the stack
1225 call cwritestr ; Expects SI -> error msg
1226 al_ok: jmp enter_command ; Return to command prompt
1228 ; End of abort_check
1235 ; kaboom: write a message and bail out. Wait for quite a while, or a user keypress,
1236 ; then do a hard reboot.
1243 .patch: mov si,bailmsg
1244 call writestr ; Returns with AL = 0
1245 .drain: call pollchar
1250 mov edi,[RebootTime]
1252 and al,09h ; Magic+Timeout
1260 .wait2: mov dx,[BIOS_timer]
1261 .wait3: call pollchar
1265 a32 loop .wait2 ; a32 means use ecx as counter
1272 mov word [BIOS_magic],0 ; Cold reboot
1273 jmp 0F000h:0FFF0h ; Reset vector address
1276 ; memory_scan_for_pxe_struct:
1278 ; If none of the standard methods find the !PXE structure, look for it
1279 ; by scanning memory.
1281 ; On exit, if found:
1282 ; CF = 0, ES:BX -> !PXE structure
1283 ; Otherwise CF = 1, all registers saved
1285 memory_scan_for_pxe_struct:
1290 mov si,trymempxe_msg
1292 mov ax,[BIOS_fbm] ; Starting segment
1293 shl ax,(10-4) ; Kilobytes -> paragraphs
1294 ; mov ax,01000h ; Start to look here
1295 dec ax ; To skip inc ax
1298 cmp ax,0A000h ; End of memory
1307 movzx cx,byte [es:4] ; Length of structure
1308 cmp cl,08h ; Minimum length
1317 jnz .mismatch ; Checksum must == 0
1320 mov [bp+8],bx ; Save BX into stack frame (will be == 0)
1328 .not_found: mov si,notfound_msg
1336 ; memory_scan_for_pxenv_struct:
1338 ; If none of the standard methods find the PXENV+ structure, look for it
1339 ; by scanning memory.
1341 ; On exit, if found:
1342 ; CF = 0, ES:BX -> PXENV+ structure
1343 ; Otherwise CF = 1, all registers saved
1345 memory_scan_for_pxenv_struct:
1347 mov si,trymempxenv_msg
1349 ; mov ax,[BIOS_fbm] ; Starting segment
1350 ; shl ax,(10-4) ; Kilobytes -> paragraphs
1351 mov ax,01000h ; Start to look here
1352 dec ax ; To skip inc ax
1355 cmp ax,0A000h ; End of memory
1364 movzx cx,byte [es:8] ; Length of structure
1365 cmp cl,26h ; Minimum length
1373 jnz .mismatch ; Checksum must == 0
1375 mov [bp+8],bx ; Save BX into stack frame
1381 .not_found: mov si,notfound_msg
1390 ; Open a TFTP connection to the server
1396 ; SI = socket pointer
1397 ; DX:AX = file length in bytes
1407 call allocate_socket
1410 mov ax,PKT_RETRY ; Retry counter
1411 mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
1413 .sendreq: push ax ; [bp-2] - Retry counter
1414 push si ; [bp-4] - File name
1415 push bx ; [bp-6] - TFTP block
1417 push bx ; [bp-8] - TID (socket port no)
1419 mov eax,[ServerIP] ; Server IP
1420 mov [pxe_udp_write_pkt.sip],eax
1421 mov [pxe_udp_write_pkt.lport],bx
1423 mov [pxe_udp_write_pkt.rport],ax
1425 mov [pxe_udp_write_pkt.buffer],di
1426 mov ax,TFTP_RRQ ; TFTP opcode
1428 push si ; Add common prefix
1433 call strcpy ; Filename
1435 mov cx,tftp_tail_len
1437 sub di,packet_buf ; Get packet size
1438 mov [pxe_udp_write_pkt.buffersize],di
1440 mov di,pxe_udp_write_pkt
1441 mov bx,PXENV_UDP_WRITE
1442 call far [PXENVEntry]
1444 cmp word [pxe_udp_write_pkt.status],byte 0
1448 ; Danger, Will Robinson! We need to support timeout
1449 ; and retry lest we just lost a packet...
1452 ; Packet transmitted OK, now we need to receive
1453 .getpacket: push word [PktTimeout] ; [bp-10]
1454 push word [BIOS_timer] ; [bp-12]
1456 .pkt_loop: mov bx,[bp-8] ; TID
1458 mov [pxe_udp_read_pkt.buffer],di
1459 mov di,packet_buf_size
1460 mov [pxe_udp_read_pkt.buffersize],di
1462 mov [pxe_udp_read_pkt.dip],eax
1463 mov [pxe_udp_read_pkt.lport],bx
1464 mov di,pxe_udp_read_pkt
1465 mov bx,PXENV_UDP_READ
1466 call far [PXENVEntry]
1468 jz .got_packet ; Wait for packet
1474 dec word [bp-10] ; Timeout
1476 pop ax ; Adjust stack
1478 shl word [PktTimeout],1 ; Exponential backoff
1482 mov si,[bp-6] ; TFTP pointer
1486 cmp [pxe_udp_read_pkt.sip],eax
1488 mov [si+tftp_remoteip],eax
1490 ; Got packet - reset timeout
1491 mov word [PktTimeout],PKT_TIMEOUT
1493 pop ax ; Adjust stack
1496 mov ax,[pxe_udp_read_pkt.rport]
1497 mov [si+tftp_remoteport],ax
1499 movzx eax,word [pxe_udp_read_pkt.buffersize]
1501 jb .failure ; Garbled reply
1503 cmp word [packet_buf], TFTP_ERROR
1504 je .bailnow ; ERROR reply: don't try again
1506 cmp word [packet_buf], TFTP_OACK
1509 ; Now we need to parse the OACK packet to get the transfer
1511 .parse_oack: mov cx,[pxe_udp_read_pkt.buffersize]
1514 jz .no_tsize ; No options acked
1515 .get_opt_name: mov di,si
1517 .opt_name_loop: lodsb
1520 or al,20h ; Convert to lowercase
1523 ; We ran out, and no final null
1524 jmp short .err_reply
1525 .got_opt_name: dec cx
1526 jz .err_reply ; Option w/o value
1533 jne .err_reply ; Bad option -> error
1534 .get_value: xor eax,eax
1545 ; Ran out before final null
1546 jmp short .err_reply
1548 jnz .err_reply ; Not end of packet
1549 ; Move size into DX:AX (old calling convention)
1550 ; but let EAX == DX:AX
1554 xor edi,edi ; ZF <- 1
1559 pop si ; We want the packet ptr in SI
1561 mov [si+tftp_filesize],eax
1562 mov [si+tftp_filepos],edi
1566 pop bp ; Junk (retry counter)
1570 .err_reply: ; Option negotiation error. Send ERROR reply.
1571 mov ax,[pxe_udp_read_pkt.rport]
1572 mov word [pxe_udp_write_pkt.rport],ax
1573 mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
1574 mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
1575 mov di,pxe_udp_write_pkt
1576 mov bx,PXENV_UDP_WRITE
1577 call far [PXENVEntry]
1579 .no_tsize: mov si,err_oldtftp
1583 .bailnow: add sp,byte 8 ; Immediate error - no retry
1586 .failure: pop bx ; Junk
1590 dec ax ; Retry counter
1591 jnz .sendreq ; Try again
1593 .error: xor si,si ; ZF <- 1
1598 ; allocate_socket: Allocate a local UDP port structure
1602 ; BX = socket pointer
1610 .check: cmp word [bx], byte 0
1612 add bx,tftp_port_t_size
1617 ; Allocate a socket number. Socket numbers are made
1618 ; guaranteed unique by including the socket slot number
1619 ; (inverted, because we use the loop counter cx); add a
1620 ; counter value to keep the numbers from being likely to
1621 ; get immediately reused.
1623 ; The NextSocket variable also contains the top two bits
1624 ; set. This generates a value in the range 49152 to
1631 and ax,((1 << (13-MAX_SOCKETS_LG2))-1) | 0xC000
1633 shl cx,13-MAX_SOCKETS_LG2
1635 xchg ch,cl ; Convert to network byte order
1636 mov [bx],cx ; Socket in use
1642 ; strcpy: Copy DS:SI -> ES:DI up to and including a null byte
1653 ; writechr: Write a single character in AL to the console without
1654 ; mangling any registers. This does raw console writes,
1655 ; since some PXE BIOSes seem to interfere regular console I/O.
1658 call write_serial ; write to serial port if needed
1663 mov ah,03h ; Read cursor position
1674 mov bl,07h ; White on black
1676 mov ah,09h ; Write char and attribute
1686 .curxyok: mov bh,[TextPage]
1687 mov ah,02h ; Set cursor position
1696 mov ax,0601h ; Scroll up one line
1697 mov bh,[ScrollAttribute]
1699 mov dx,[ScreenSize] ; The whole screen
1713 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1714 ; to by ES:DI; ends on encountering any whitespace.
1716 ; This verifies that a filename is < FILENAME_MAX characters
1717 ; and doesn't contain whitespace, and zero-pads the output buffer,
1718 ; so "repe cmpsb" can do a compare.
1721 mov cx,FILENAME_MAX-1
1724 cmp al,' ' ; If control or space, end
1729 inc cx ; At least one null byte
1730 xor ax,ax ; Zero-fill name
1731 rep stosb ; Doesn't do anything if CX=0
1735 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1736 ; filename to the conventional representation. This is needed
1737 ; for the BOOT_IMAGE= parameter for the kernel.
1738 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1739 ; known to be shorter.
1741 ; DS:SI -> input mangled file name
1742 ; ES:DI -> output buffer
1744 ; On return, DI points to the first byte after the output name,
1745 ; which is set to a null byte.
1747 unmangle_name: call strcpy
1748 dec di ; Point to final null byte
1754 ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
1755 ; calling convention (using the stack.)
1757 ; This is called as a far routine so that we can just stick it into
1758 ; the PXENVEntry variable.
1763 call far [cs:PXEEntry]
1766 cmc ; Set CF unless ax == 0
1770 ; getfssec: Get multiple clusters from a file, given the starting cluster.
1772 ; In this case, get multiple blocks from a specific TCP connection.
1776 ; SI -> TFTP block pointer
1777 ; CX -> 512-byte block pointer; 0FFFFh = until end of file
1779 ; SI -> TFTP block pointer (or 0 on EOF)
1784 .packet_loop: push cx ; <A> Save count
1785 push es ; <B> Save buffer pointer
1786 push bx ; <C> Block pointer
1791 ; Start by ACKing the previous packet; this should cause the
1792 ; next packet to be sent.
1794 mov word [PktTimeout],PKT_TIMEOUT
1796 .send_ack: push cx ; <D> Retry count
1798 mov eax,[si+tftp_filepos]
1799 shr eax,TFTP_BLOCKSIZE_LG2
1800 xchg ah,al ; Network byte order
1801 call ack_packet ; Send ACK
1803 ; We used to test the error code here, but sometimes
1804 ; PXE would return negative status even though we really
1805 ; did send the ACK. Now, just treat a failed send as
1806 ; a normally lost packet, and let it time out in due
1809 .send_ok: ; Now wait for packet.
1810 mov dx,[BIOS_timer] ; Get current time
1813 .wait_data: push cx ; <E> Timeout
1814 push dx ; <F> Old time
1817 mov [pxe_udp_read_pkt.buffer],bx
1818 mov [pxe_udp_read_pkt.buffersize],word packet_buf_size
1819 mov eax,[bx+tftp_remoteip]
1820 mov [pxe_udp_read_pkt.sip],eax
1822 mov [pxe_udp_read_pkt.dip],eax
1823 mov ax,[si+tftp_remoteport]
1824 mov [pxe_udp_read_pkt.rport],ax
1825 mov ax,[si+tftp_localport]
1826 mov [pxe_udp_read_pkt.lport],ax
1827 mov di,pxe_udp_read_pkt
1828 mov bx,PXENV_UDP_READ
1830 call far [PXENVEntry]
1835 ; No packet, or receive failure
1837 pop ax ; <F> Old time
1838 pop cx ; <E> Timeout
1839 cmp ax,dx ; Same time -> don't advance timeout
1840 je .wait_data ; Same clock tick
1841 loop .wait_data ; Decrease timeout
1843 pop cx ; <D> Didn't get any, send another ACK
1844 shl word [PktTimeout],1 ; Exponential backoff
1846 jmp kaboom ; Forget it...
1848 .recv_ok: pop dx ; <F>
1851 cmp word [pxe_udp_read_pkt.buffersize],byte 4
1852 jb .wait_data ; Bad size for a DATA packet
1854 cmp word [packet_buf],TFTP_DATA ; Not a data packet?
1855 jne .wait_data ; Then wait for something else
1857 mov eax,[si+tftp_filepos]
1858 shr eax,TFTP_BLOCKSIZE_LG2
1859 inc ax ; Which packet are we waiting for?
1860 xchg ah,al ; Network byte order
1861 cmp word [packet_buf+2],ax
1864 ; Wrong packet, ACK the packet and then try again
1865 ; This is presumably because the ACK got lost,
1866 ; so the server just resent the previous packet
1867 mov ax,[packet_buf+2]
1869 jmp .send_ok ; Reset timeout
1871 .right_packet: ; It's the packet we want. We're also EOF if the size < 512.
1872 pop cx ; <D> Don't need the retry count anymore
1873 movzx ecx,word [pxe_udp_read_pkt.buffersize]
1875 add [si+tftp_filepos],ecx
1877 cmp cx,TFTP_BLOCKSIZE ; Is it a full block
1880 pop di ; <C> Get target buffer
1886 mov cx,TFTP_BLOCKSIZE >> 2
1892 loop .packet_loop_jmp
1894 ; If we had the exact right number of bytes, always get
1895 ; one more packet to get the (zero-byte) EOF packet and
1897 mov eax,[si+tftp_filepos]
1898 cmp [si+tftp_filesize],eax
1902 ret ; Mission accomplished
1904 .packet_loop_jmp: jmp .packet_loop
1906 .last_block: ; Last block - ACK packet immediately and free socket
1907 mov ax,[packet_buf+2]
1909 mov word [si],0 ; Socket closed
1922 pop cx ; <A> Not used
1929 ; Send ACK packet. This is a common operation and so is worth canning.
1933 ; AX = Packet # to ack (network byte order)
1936 ; All registers preserved
1938 ; This function uses the pxe_udp_write_pkt but not the packet_buf.
1942 mov [ack_packet_buf+2],ax ; Packet number to ack
1944 mov [pxe_udp_write_pkt.lport],ax
1945 mov ax,[si+tftp_remoteport]
1946 mov [pxe_udp_write_pkt.rport],ax
1947 mov eax,[si+tftp_remoteip]
1948 mov [pxe_udp_write_pkt.sip],eax
1949 mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
1950 mov [pxe_udp_write_pkt.buffersize], word 4
1951 mov di,pxe_udp_write_pkt
1952 mov bx,PXENV_UDP_WRITE
1953 call far [PXENVEntry]
1954 cmp ax,byte 0 ; ZF = 1 if write OK
1961 ; This function unloads the PXE and UNDI stacks and unclaims
1962 ; the memory. Assumes CS == DS == ES.
1965 test byte [KeepPXE],01h ; Should we keep PXE around?
1968 mov si,new_api_unload
1969 cmp byte [APIVer+1],2 ; Major API version >= 2?
1971 mov si,old_api_unload
1974 .call_loop: xor ax,ax
1979 mov di,pxe_unload_stack_pkt
1982 mov cx,pxe_unload_stack_pkt_len >> 1
1985 call far [PXENVEntry]
1987 cmp word [pxe_unload_stack_pkt.status],PXENV_STATUS_SUCCESS
1994 mov dx,[RealBaseMem]
1995 cmp dx,[BIOS_fbm] ; Sanity check
1999 ; Check that PXE actually unhooked the INT 1Ah chain
2000 movzx eax,word [4*0x1a]
2001 movzx ecx,word [4*0x1a+2]
2005 cmp ax,dx ; Not in range
2016 mov si,cant_free_msg
2026 ; We want to keep PXE around, but still we should reset
2027 ; it to the standard bootup configuration
2029 mov bx,PXENV_UDP_CLOSE
2030 mov di,pxe_udp_close_pkt
2031 call far [PXENVEntry]
2037 ; Take an IP address (in network byte order) in EAX and
2038 ; output a dotted quad string to ES:DI.
2039 ; DI points to terminal null at end of string on exit.
2049 ; Now AH = 100-digit; AL = remainder
2056 ; Now AH = 10-digit; AL = remainder
2060 ; Now AH = 10-digit; AL = remainder
2073 ror eax,8 ; Move next char into LSB
2083 ; Parse a DHCP packet. This includes dealing with "overloaded"
2084 ; option fields (see RFC 2132, section 9.3)
2086 ; This should fill in the following global variables, if the
2087 ; information is present:
2089 ; MyIP - client IP address
2090 ; ServerIP - boot server IP address
2091 ; Netmask - network mask
2092 ; Gateway - default gateway router IP
2093 ; BootFile - boot file name
2095 ; This assumes the DHCP packet is in "trackbuf" and the length
2096 ; of the packet in in CX on entry.
2100 mov byte [OverLoad],0 ; Assume no overload
2101 mov eax, [trackbuf+bootp.yip]
2104 cmp al,224 ; Class D or higher -> bad
2108 mov eax, [trackbuf+bootp.sip]
2111 cmp al,224 ; Class D or higher -> bad
2115 sub cx, bootp.options
2117 mov si, trackbuf+bootp.option_magic
2119 cmp eax, BOOTP_OPTION_MAGIC
2121 call parse_dhcp_options
2123 mov si, trackbuf+bootp.bootfile
2124 test byte [OverLoad],1
2127 call parse_dhcp_options
2128 jmp short .parsed_file
2131 jz .parsed_file ; No bootfile name
2136 stosb ; Null-terminate
2138 mov si, trackbuf+bootp.sname
2139 test byte [OverLoad],2
2142 call parse_dhcp_options
2147 ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
2148 ; size is CX -- some DHCP servers leave option fields unterminated
2149 ; in violation of the spec.
2158 jz .done ; Last byte; must be PAD, END or malformed
2159 cmp al, 0 ; PAD option
2161 cmp al,255 ; END option
2164 ; Anything else will have a length field
2165 mov dl,al ; DL <- option number
2167 lodsb ; AX <- option length
2169 sub cx,ax ; Decrement bytes left counter
2170 jb .done ; Malformed option: length > field size
2172 cmp dl,1 ; SUBNET MASK option
2179 cmp dl,3 ; ROUTER option
2186 cmp dl,52 ; OPTION OVERLOAD option
2193 cmp dl,67 ; BOOTFILE NAME option
2196 jmp short .copyoption
2198 ret ; This is here to make short jumps easier
2201 cmp dl,208 ; PXELINUX MAGIC option
2203 cmp al,4 ; Must have length == 4
2205 cmp dword [si], htonl(0xF100747E) ; Magic number
2207 or byte [DHCPMagic], byte 1 ; Found magic #
2211 cmp dl,209 ; PXELINUX CONFIGFILE option
2214 or byte [DHCPMagic], byte 2 ; Got config file
2215 jmp short .copyoption
2218 cmp dl,210 ; PXELINUX PATHPREFIX option
2221 or byte [DHCPMagic], byte 4 ; Got path prefix
2222 jmp short .copyoption
2225 cmp dl,211 ; PXELINUX REBOOTTIME option
2230 xchg dl,dh ; Convert to host byte order
2233 mov [RebootTime],edx
2234 or byte [DHCPMagic], byte 8 ; Got RebootTime
2235 ; jmp short .opt_done
2238 ; Unknown option. Skip to the next one.
2244 ; Common code for copying an option verbatim
2248 xchg cx,ax ; Now ax == 0
2249 stosb ; Null-terminate
2250 jmp short .opt_done_noskip
2255 ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
2256 ; option into IPOption based on a DHCP packet in trackbuf.
2257 ; Assumes CS == DS == ES.
2278 call gendotquad ; Zero-terminates its output
2280 mov [IPOptionLen],di
2284 ; -----------------------------------------------------------------------------
2286 ; -----------------------------------------------------------------------------
2288 %include "getc.inc" ; getc et al
2289 %include "conio.inc" ; Console I/O
2290 %include "writestr.inc" ; String output
2291 writestr equ cwritestr
2292 %include "writehex.inc" ; Hexadecimal output
2293 %include "parseconfig.inc" ; High-level config file handling
2294 %include "parsecmd.inc" ; Low-level config file handling
2295 %include "bcopy32.inc" ; 32-bit bcopy
2296 %include "loadhigh.inc" ; Load a file into high memory
2297 %include "font.inc" ; VGA font stuff
2298 %include "graphics.inc" ; VGA graphics
2299 %include "highmem.inc" ; High memory sizing
2301 ; -----------------------------------------------------------------------------
2302 ; Begin data section
2303 ; -----------------------------------------------------------------------------
2305 CR equ 13 ; Carriage Return
2306 LF equ 10 ; Line Feed
2307 FF equ 12 ; Form Feed
2308 BS equ 8 ; Backspace
2310 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
2312 boot_prompt db 'boot: ', 0
2313 wipe_char db BS, ' ', BS, 0
2314 err_notfound db 'Could not find kernel image: ',0
2315 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
2316 err_noram db 'It appears your computer has less than 384K of low ("DOS")'
2318 db 'RAM. Linux needs at least this amount to boot. If you get'
2320 db 'this message in error, hold down the Ctrl key while'
2322 db 'booting, and I will take your word for it.', 0Dh, 0Ah, 0
2323 err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
2324 err_noparm db 'Missing parameter in config file.', CR, LF, 0
2325 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
2326 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
2327 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
2328 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
2330 err_notdos db ': attempted DOS system call', CR, LF, 0
2331 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
2332 err_bssimage db 'BSS images not supported.', CR, LF, 0
2333 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
2334 err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
2335 bailmsg equ err_bootfailed
2336 err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
2337 err_pxefailed db 'PXE API call failed, error ', 0
2338 err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
2339 err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
2340 found_pxenv db 'Found PXENV+ structure', CR, LF, 0
2341 using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
2342 apiver_str db 'PXE API version is ',0
2343 pxeentry_msg db 'PXE entry point found (we hope) at ', 0
2344 pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
2345 trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
2346 trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
2347 undi_data_msg db 'UNDI data segment at: ',0
2348 undi_data_len_msg db 'UNDI data segment size: ',0
2349 undi_code_msg db 'UNDI code segment at: ',0
2350 undi_code_len_msg db 'UNDI code segment size: ',0
2351 cant_free_msg db 'Failed to free base memory, error ', 0
2352 notfound_msg db 'not found', CR, LF, 0
2353 myipaddr_msg db 'My IP address seems to be ',0
2354 tftpprefix_msg db 'TFTP prefix: ', 0
2355 localboot_msg db 'Booting from local disk...', CR, LF, 0
2356 cmdline_msg db 'Command line: ', CR, LF, 0
2357 ready_msg db 'Ready.', CR, LF, 0
2358 trying_msg db 'Trying to load: ', 0
2359 crlfloading_msg db CR, LF ; Fall through
2360 loading_msg db 'Loading ', 0
2363 fourbs_msg db BS, BS, BS, BS, 0
2364 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
2367 crff_msg db CR, FF, 0
2368 default_str db 'default', 0
2369 default_len equ ($-default_str)
2370 syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
2371 cfgprefix db 'pxelinux.cfg/' ; No final null!
2372 cfgprefix_len equ ($-cfgprefix)
2375 ; Command line options we'd like to take a look at
2377 ; mem= and vga= are handled as normal 32-bit integer values
2378 initrd_cmd db 'initrd='
2379 initrd_cmd_len equ 7
2382 ; Config file keyword table
2384 %include "keywords.inc"
2387 ; Extensions to search for (in *forward* order).
2388 ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
2391 exten_table: db '.cbt' ; COMBOOT (specific)
2392 db '.0', 0, 0 ; PXE bootstrap program
2393 db '.com' ; COMBOOT (same as DOS)
2395 dd 0, 0 ; Need 8 null bytes here
2398 ; PXENV entry point. If we use the !PXE API, this will point to a thunk
2399 ; function that converts to the !PXE calling convention.
2401 PXENVEntry dw pxe_thunk,0
2404 ; PXE unload sequences
2408 db PXENV_UNDI_SHUTDOWN
2409 db PXENV_UNLOAD_STACK
2414 db PXENV_UNDI_SHUTDOWN
2415 db PXENV_UNLOAD_STACK
2416 db PXENV_UNDI_CLEANUP
2420 ; PXE query packets partially filled in
2422 pxe_bootp_query_pkt_2:
2423 .status: dw 0 ; Status
2424 .packettype: dw 2 ; DHCPACK packet
2425 .buffersize: dw trackbufsize ; Packet size
2426 .buffer: dw trackbuf, 0 ; seg:off of buffer
2427 .bufferlimit: dw trackbufsize ; Unused
2429 pxe_bootp_query_pkt_3:
2430 .status: dw 0 ; Status
2431 .packettype: dw 3 ; Boot server packet
2432 .buffersize: dw trackbufsize ; Packet size
2433 .buffer: dw trackbuf, 0 ; seg:off of buffer
2434 .bufferlimit: dw trackbufsize ; Unused
2436 pxe_bootp_size_query_pkt:
2437 .status: dw 0 ; Status
2438 .packettype: dw 2 ; DHCPACK packet
2439 .buffersize: dw 0 ; Packet size
2440 .buffer: dw 0, 0 ; seg:off of buffer
2441 .bufferlimit: dw 0 ; Unused
2444 .status: dw 0 ; Status
2445 .sip: dd 0 ; Source (our) IP
2448 .status: dw 0 ; Status
2451 .status: dw 0 ; Status
2452 .sip: dd 0 ; Server IP
2453 .gip: dd 0 ; Gateway IP
2454 .lport: dw 0 ; Local port
2455 .rport: dw 0 ; Remote port
2456 .buffersize: dw 0 ; Size of packet
2457 .buffer: dw 0, 0 ; seg:off of buffer
2460 .status: dw 0 ; Status
2461 .sip: dd 0 ; Source IP
2462 .dip: dd 0 ; Destination (our) IP
2463 .rport: dw 0 ; Remote port
2464 .lport: dw 0 ; Local port
2465 .buffersize: dw 0 ; Max packet size
2466 .buffer: dw 0, 0 ; seg:off of buffer
2469 ; Misc initialized (data) variables
2471 AppendLen dw 0 ; Bytes in append= command
2472 KbdTimeOut dw 0 ; Keyboard timeout (if any)
2473 CmdLinePtr dw cmd_line_here ; Command line advancing pointer
2475 initrd_ptr dw 0 ; Initial ramdisk pointer/flag
2476 VKernelCtr dw 0 ; Number of registered vkernels
2477 ForcePrompt dw 0 ; Force prompt
2478 AllowImplicit dw 1 ; Allow implicit kernels
2479 SerialPort dw 0 ; Serial port base (or 0 for no serial port)
2480 NextSocket dw 49152 ; Counter for allocating socket numbers
2481 VGAFontSize dw 16 ; Defaults to 16 byte font
2482 UserFont db 0 ; Using a user-specified font
2483 ScrollAttribute db 07h ; White on black (for text mode)
2484 KeepPXE db 0 ; Should PXE be kept around?
2489 tftp_tail db 'octet', 0, 'tsize' ,0, '0', 0 ; Octet mode, request size
2490 tftp_tail_len equ ($-tftp_tail)
2491 tsize_str db 'tsize', 0
2492 tsize_len equ ($-tsize_str)
2493 tftp_opt_err dw TFTP_ERROR ; ERROR packet
2494 dw htons(8) ; ERROR 8: bad options
2495 db 'tsize option required', 0 ; Error message
2496 tftp_opt_err_len equ ($-tftp_opt_err)
2499 ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
2502 ; IP information (initialized to "unknown" values)
2503 MyIP dd 0 ; My IP address
2504 ServerIP dd 0 ; IP address of boot server
2505 Netmask dd 0 ; Netmask of this subnet
2506 Gateway dd 0 ; Default router
2507 ServerPort dw TFTP_PORT ; TFTP server port
2510 ; Variables that are uninitialized in SYSLINUX but initialized here
2513 ClustSize dd TFTP_BLOCKSIZE ; Bytes/cluster
2514 ClustPerMoby dd 65536/TFTP_BLOCKSIZE ; Clusters per 64K
2515 SecPerClust dw TFTP_BLOCKSIZE/512 ; Same as bsSecPerClust, but a word
2516 BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
2517 BufSafeSec dw trackbufsize/512 ; = how many sectors?
2518 BufSafeBytes dw trackbufsize ; = how many bytes?
2519 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
2521 %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
2522 %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
2525 IPAppend db 0 ; Default IPAPPEND option
2526 DHCPMagic db 0 ; DHCP site-specific option info
2529 ; Stuff for the command line; we do some trickery here with equ to avoid
2530 ; tons of zeros appended to our file and wasting space
2532 linuxauto_cmd db 'linux auto',0
2533 linuxauto_len equ $-linuxauto_cmd
2534 boot_image db 'BOOT_IMAGE='
2535 boot_image_len equ $-boot_image
2536 align 4, db 0 ; For the good of REP MOVSD
2538 default_cmd equ $+(max_cmd_len+2)
2539 ldlinux_end equ default_cmd+(max_cmd_len+1)
2540 kern_cmd_len equ ldlinux_end-command_line
2542 ; Put the getcbuf right after the code, aligned on a sector boundary
2544 end_of_code equ (ldlinux_end-bootsec)+7C00h
2545 getcbuf equ (end_of_code + 511) & 0FE00h
2547 ; VGA font buffer at the end of memory (so loading a font works even
2548 ; in graphics mode.)
2549 vgafontbuf equ 0E000h
2551 ; This is a compile-time assert that we didn't run out of space
2553 %if (getcbuf+trackbufsize) > vgafontbuf
2554 %error "Out of memory, better reorganize something..."