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