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