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