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