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