Okay, we have established communications with PXE... and are capable of
authorhpa <hpa>
Mon, 6 Sep 1999 11:35:04 +0000 (11:35 +0000)
committerhpa <hpa>
Mon, 6 Sep 1999 11:35:04 +0000 (11:35 +0000)
scanning for our config file.  A good place to stop.

pxelinux.asm

index 6a6dd90..cdd5c2c 100644 (file)
@@ -243,6 +243,7 @@ HiLoadAddr      resd 1                      ; Address pointer for high load loop
 HighMemSize    resd 1                  ; End of memory pointer (bytes)
 KernelSize     resd 1                  ; Size of kernel (bytes)
 Stack          resd 1                  ; Pointer to reset stack
+PXEEntry       resd 1                  ; !PXE API entry point
 RootDir                equ $                   ; Location of root directory
 RootDir1       resw 1
 RootDir2       resw 1
@@ -296,6 +297,30 @@ InitRDCName     resb MAX_FILENAME       ; Unmangled initrd name
 MNameBuf       resb 11                 ; OBSOLETE
 InitRD         resb 11                 ; OBSOLETE
 
+               alignb 16
+               ; BOOTP/DHCP packet buffer
+bootp:
+.opcode                resb 1                  ; BOOTP/DHCP "opcode"
+.hardware      resb 1                  ; ARP hardware type
+.hardlen       resb 1                  ; Hardware address length
+.gatehops      resb 1                  ; Used by forwarders
+.ident         resd 1                  ; Transaction ID
+.seconds       resw 1                  ; Seconds elapsed
+.flags         resw 1                  ; Broadcast flags
+.cip           resd 1                  ; Client IP
+.yip           resd 1                  ; "Your" IP
+.sip           resd 1                  ; Next server IP
+.gip           resd 1                  ; Relay agent IP
+.macaddr       resb 16                 ; Client MAC address
+.sname         resb 64                 ; Server name (optional)
+.bootfile      resb 128                ; Boot file name
+.v_magic       resd 1                  ; DHCP magic cookie
+.v_flags       resd 1                  ; DHCP flags
+.v_pad         resb 56                 ; Vendor options padding
+
+bootp_size     equ $-bootp
+
+
                section .text
                 org 7C00h
 ;
@@ -332,17 +357,24 @@ _start1:
                cmp ax,564Eh
                jne no_pxe
 
-               mov si,found_pxenv
-               call writestr
-
                ; Okay, that gave us the PXENV+ structure, find !PXE
                ; structure from that
                cmp dword [es:bx], 'PXEN'
                jne no_pxe
                cmp word [es:bx+4], 'V+'
                jne no_pxe
+
+               mov si,found_pxenv
+               call writestr
+
+               mov si,apiver_str
+               call writestr
+               mov ax,[es:bx+6]
+               call writehex4
+               call crlf
+
                cmp word [es:bx+6], 0201h       ; API version 2.1 or higher
-               jb no_pxe
+               jb old_api
                les bx,[es:bx+26h]              ; !PXE structure pointer
                cmp dword [es:bx],'!PXE'
                je have_pxe
@@ -351,7 +383,26 @@ no_pxe:            mov si,err_nopxe
                call writestr
                jmp kaboom
 
+old_api:       ; Need to use a PXENV+ structure
+               mov si,using_pxenv_msg
+               call writestr
+
+               mov eax,[es:bx+0Ah]             ; PXE RM API
+               mov [PXENVEntry],eax
+               jmp short have_pxe_entry
 have_pxe:
+               mov eax,[es:bx+10h]
+               mov [PXEEntry],eax
+
+have_pxe_entry:        mov si,pxeentry_msg
+               call writestr
+               mov ax,[PXENVEntry+2]
+               call writehex4
+               mov al,':'
+               call writechr
+               mov ax,[PXENVEntry]
+               call writehex4
+               call crlf
 
 ;
 ; Tell the user we got this far
@@ -359,174 +410,148 @@ have_pxe:
                mov si,pxelinux_banner
                call writestr
 
-.enough:       jmp short .enough
-
+               mov si,copyright_str
+               call writestr
+               
 ;
-; -----------------------------------------------------------------------------
-; Subroutines that have to be in the first sector
-; -----------------------------------------------------------------------------
+; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
+; address)
 ;
-; getfssec: Get multiple clusters from a file, given the starting cluster.
+query_bootp:   mov ax,0DEADh                   ; Something bogus
+       
+               mov ax,ds
+               mov es,ax
+               mov di,pxe_bootp_query_pkt
+               mov bx,0071h                    ; PXENV_GET_CACHED_INFO
+
+               call far [PXENVEntry]
+               jc .pxe_err
+               cmp ax,byte 0
+               je .pxe_ok
+.pxe_err:
+               mov si,err_pxefailed
+               call writestr
+               call writehex4
+               call crlf
+
+.pxe_ok:       mov si,myipaddr_msg
+               call writestr
+               mov eax,[bootp.yip]             ; "Your" IP address
+               call writehex8
+               mov al,' '
+               call writechr
+               mov eax,[bootp.sip]             ; Server IP address(?)
+               call writehex8
+               mov al,' '
+               call writechr
+               mov eax,[bootp.gip]             ; Gateway IP address(?)
+               call writehex8
+               call crlf
+
+               ; Print the boot file
+               mov si,bootp.bootfile
+               call writestr
+               call crlf
 ;
-;      This routine makes sure the subtransfers do not cross a 64K boundary,
-;      and will correct the situation if it does, UNLESS *sectors* cross
-;      64K boundaries.
+; Normalize ES = DS
 ;
-;      ES:BX   -> Buffer
-;      SI      -> Starting cluster number (2-based)
-;      CX      -> Cluster count (0FFFFh = until end of file)
+               mov ax,ds
+               mov es,ax
 ;
-                                               ; 386 check
-getfssec:
-%if 0
-getfragment:   xor bp,bp                       ; Fragment sector count
-               mov ax,si                       ; Get sector address
-               dec ax                          ; Convert to 0-based
-               dec ax
-               mul word [SecPerClust]
-               add ax,[DataArea1]
-               adc dx,[DataArea2]
-getseccnt:                                     ; See if we can read > 1 clust
-               add bp,[SecPerClust]
-               dec cx                          ; Reduce clusters left to find
-               mov di,si                       ; Predict next cluster
+; Now, prepare some of the data structures
+;
+               mov eax,[bootp.sip]
+               mov [pxe_tftp_open_pkt.sip],eax
+
+filename_cp:   mov si,bootp.bootfile
+               mov di,pxe_tftp_open_pkt.filename
+               cld
+               xor cx,cx
+.strcpy:       lodsb
+               stosb
+               inc cx
+               and al,al
+               jnz .strcpy
+               std
+               dec di
+               mov si,di
+               mov al,'.'
+               repne scasb
+               jne .nodot
                inc di
-               call [NextCluster]
-               jc gfs_eof                      ; At EOF?
-               jcxz endfragment                ; Or was it the last we wanted?
-               cmp si,di                       ; Is file continuous?
-               jz getseccnt                    ; Yes, we can get
-endfragment:   clc                             ; Not at EOF
-gfs_eof:       pushf                           ; Remember EOF or not
-               push si
-               push cx
-gfs_getchunk:
-               push ax
-               push dx
-               mov ax,es                       ; Check for 64K boundaries.
-               mov cl,4
-               shl ax,cl
-               add ax,bx
-               xor dx,dx
-               neg ax
-               jnz gfs_partseg
-               inc dx                          ; Full 64K segment
-gfs_partseg:
-               div word [bsBytesPerSec]        ; How many sectors fit?
-               mov si,bp
-               sub si,ax                       ; Compute remaining sectors
-               jbe gfs_lastchunk
-               mov bp,ax
-               pop dx
-               pop ax
-               call getlinsecsr
-               add ax,bp
-               adc dx,byte 0
-               mov bp,si                       ; Remaining sector count
-               jmp short gfs_getchunk
-gfs_lastchunk: pop dx
-               pop ax          
-               call getlinsec
-               pop cx
-               pop si
-               popf
-               jcxz gfs_return                 ; If we hit the count limit
-               jnc getfragment                 ; If we didn't hit EOF
-gfs_return:    ret
+               jmp short .dot
+.nodot:                mov di,si
+.dot:          cld
+               ; Now di points to where we want to add stuff to the filename
+               mov al,'/'
+               stosb
+               mov cx,8
+               mov eax,[bootp.yip]
+               bswap eax                       ; Convert to native byte order
+.hexify_loop:  rol eax,4
+               push eax
+               and al,0Fh
+               cmp al,10
+               jae .high
+.low:          add al,'0'
+               jmp short .char
+.high:         add al,'A'-10
+.char:         stosb
+               pop eax
+               loop .hexify_loop
 
 ;
-; getlinsecsr: save registers, call getlinsec, restore registers
+; Begin looking for configuration file
 ;
-getlinsecsr:   push ax
-               push dx
-               push cx
-               push bp
-               push si
-               push di
-               call getlinsec
-               pop di
-               pop si
-               pop bp
-               pop cx
-               pop dx
-               pop ax
-               ret
-%endif
+config_scan:
+               mov cx,9                        ; Up to 9 attempts
 
-;
-; nextcluster: Advance a cluster pointer in SI to the next cluster
-;             pointed at in the FAT tables (note: FAT12 assumed)
-;             Sets CF on return if end of file.
-;
-;             The variable NextCluster gets set to the appropriate
-;             value here.
-;
-nextcluster_fat12:
-               push ax
-               push ds
-               mov ax,fat_seg
-               mov ds,ax
-               mov ax,si                       ; Multiply by 3/2
-               shr ax,1
-               pushf                           ; CF now set if odd
-               add si,ax
-               mov si,[si]
-               popf
-               jnc nc_even
-               shr si,1                        ; Needed for odd only
-               shr si,1
-               shr si,1
-               shr si,1
-nc_even:
-               and si,0FFFh
-               cmp si,0FF0h                    ; Clears CF if at end of file
-               cmc                             ; But we want it SET...
-               pop ds
-               pop ax
-nc_return:     ret
+.tryagain:     mov byte [di],0
+               cmp cx,byte 1
+               jne .not_default
+               pusha
+               mov si,default_str
+               mov cx,default_len
+               rep movsb                       ; Copy "default" string
+               popa
+.not_default:  pusha
+               mov di,pxe_tftp_open_pkt
+               mov bx,0020h                    ; PXENV_TFTP_OPEN
+               call far [PXENVEntry]
+               jc .badness
+               cmp ax,byte 0
+               jne .badness
+               cmp word [pxe_tftp_open_pkt.status],0
+               je .success
+.badness:      popa
+               dec di
+               loop .tryagain
 
 ;
-; FAT16 decoding routine.  Note that a 16-bit FAT can be up to 128K,
-; so we have to decide if we're in the "low" or the "high" 64K-segment...
-;
-nextcluster_fat16:
-               push ax
-               push ds
-               mov ax,fat_seg
-               shl si,1
-               jnc .seg0
-               mov ax,fat_seg+1000h
-.seg0:         mov ds,ax
-               mov si,[si]
-               cmp si,0FFF0h
-               cmc
-               pop ds
-               pop ax
-               ret
-;
-; Debug routine
+; If we get here, no configuration file
 ;
-%ifdef debug
-safedumpregs:
-               cmp word [Debug_Magic],0D00Dh
-               jnz nc_return
-               jmp dumpregs
-%endif
 
-rl_checkpt     equ $                           ; Must be <= 400h
+.success:      popa
+               ; If we get here, we have an open TFTP connection to the config file
 
-; ----------------------------------------------------------------------------
-;  End of code and data that have to be in the first sector
-; ----------------------------------------------------------------------------
+enough:                mov si,enough_msg
+               call writestr
+               jmp kaboom
+enough_msg:    db 13,10,10,'End of implemented code.', 13, 10, 0
 
-all_read:
 ;
-; Let the user (and programmer!) know we got this far.  This used to be
-; in Sector 1, but makes a lot more sense here.
+; getfssec: Get multiple clusters from a file, given the starting cluster.
 ;
-               mov si,copyright_str
-               call writestr
+;      This routine makes sure the subtransfers do not cross a 64K boundary,
+;      and will correct the situation if it does, UNLESS *sectors* cross
+;      64K boundaries.
 ;
+;      ES:BX   -> Buffer
+;      SI      -> Starting cluster number (2-based)
+;      CX      -> Cluster count (0FFFFh = until end of file)
+;
+                                               ; 386 check
+; 
 ; Check that no moron is trying to boot Linux on a 286 or so.  According
 ; to Intel, the way to check is to see if the high 4 bits of the FLAGS
 ; register are either all stuck at 1 (8086/8088) or all stuck at 0
@@ -611,13 +636,6 @@ is_486:
 ;
                call adjust_screen
 ;
-; Now, everything is "up and running"... patch kaboom for more
-; verbosity and using the full screen system
-;
-               mov byte [kaboom.patch],0e9h            ; JMP NEAR
-               mov word [kaboom.patch+1],kaboom2-(kaboom.patch+3)
-
-;
 ; Now we're all set to start with our *real* business. First load the
 ; configuration file (if any) and parse it.
 ;
@@ -945,7 +963,7 @@ enter_char: test byte [FuncFlag],1
 get_char_2:    jmp short get_char
 not_ascii:     mov byte [FuncFlag],0
                cmp al,0Dh                      ; Enter
-               je command_done
+               je near command_done
                cmp al,06h                      ; <Ctrl-F>
                je set_func_flag
                cmp al,08h                      ; Backspace
@@ -1241,7 +1259,7 @@ kernel_sane:      push ax
                sub [KernelClust],cx
                xor bx,bx
                 pop si                          ; Cluster pointer on stack
-               call getfssec
+;              call getfssec
                jc near kernel_corrupt          ; Failure in first 32K
                 cmp word [es:bs_bootsign],0AA55h
                jne near kernel_corrupt         ; Boot sec signature missing
@@ -1486,7 +1504,7 @@ high_last_moby:
                sub [KernelClust],cx
                xor bx,bx                       ; Load at offset 0
                 pop si                          ; Restore cluster pointer
-                call getfssec
+;              call getfssec
                 push si                         ; Save cluster pointer
                 pushf                           ; Save EOF
                 xor bx,bx
@@ -1625,7 +1643,7 @@ is_comboot_image:
                mov bx,100h             ; Load at <seg>:0100h
 
                mov cx,[ClustPerMoby]   ; Absolute maximum # of clusters
-               call getfssec
+;              call getfssec
 
                xor di,di
                mov cx,64               ; 256 bytes (size of PSP)
@@ -1935,7 +1953,7 @@ rd_last_moby:
                 push word xfer_buf_seg         ; Bounce buffer segment
                pop es
                push cx
-               call getfssec
+;              call getfssec
                pop cx
                 push si                                ; Save cluster pointer
                mov esi,(xfer_buf_seg << 4)
@@ -2009,20 +2027,6 @@ kaboom:
 .norge:                jmp short .norge        ; If int 19h returned; this is the end
 
 ;
-;
-; writestr: write a null-terminated string to the console
-;
-writestr:
-wstr_1:         lodsb
-               and al,al
-                jz .return
-               mov ah,0Eh              ; Write to screen as TTY
-               mov bx,0007h            ; White on black, current page
-               int 10h
-               jmp short wstr_1
-.return:       ret
-
-;
 ; searchdir: Search the root directory for a pre-mangled filename in
 ;           DS:DI.  This routine is similar to the one in the boot
 ;           sector, but is a little less Draconian when it comes to
@@ -2080,7 +2084,7 @@ bf_ret:           ret
 loadfont:
                mov bx,trackbuf                 ; The trackbuf is >= 16K; the part
                mov cx,[BufSafe]                ; of a PSF file we care about is no
-               call getfssec                   ; more than 8K+4 bytes
+;              call getfssec                   ; more than 8K+4 bytes
 
                mov ax,[trackbuf]               ; Magic number
                cmp ax,0436h
@@ -2120,7 +2124,7 @@ loadkeys:
 
                mov bx,trackbuf
                mov cx,1                        ; 1 cluster should be >= 256 bytes
-               call getfssec
+;              call getfssec
 
                mov si,trackbuf
                mov di,KbdMap
@@ -2147,7 +2151,7 @@ get_msg_chunk:  push ax                         ; DX:AX = length of file
                 push dx
                mov bx,trackbuf
                mov cx,[BufSafe]
-               call getfssec
+;              call getfssec
                 pop dx
                 pop ax
                push si                         ; Save current cluster
@@ -2290,27 +2294,68 @@ write_serial_str:
 ;
 writechr:
                call write_serial       ; write to serial port if needed
-               pusha
+               pushad
                mov ah,0Eh
                mov bx,0007h            ; white text on this page
                int 10h
-               popa
+               popad
                ret
 
 ;
+; crlf: Print a newline
+;
+crlf:          mov si,crlf_msg
+               ; Fall through
+
+;
 ; cwritestr: write a null-terminated string to the console, saving
 ;            registers on entry.
 ;
+; Note: writestr and cwritestr are distinct in SYSLINUX, not in PXELINUX
+;
 cwritestr:
-                pusha
+                pushad
 .top:          lodsb
                and al,al
                 jz .end
                call writechr
                 jmp short .top
-.end:          popa
+.end:          popad
                 ret
 
+writestr       equ cwritestr
+
+;
+; writehex[248]: Write a hex number in (AL, AX, EAX) to the console
+;
+writehex2:
+               pushad
+               rol eax,24
+               mov cx,2
+               jmp short writehex_common
+writehex4:
+               pushad
+               rol eax,16
+               mov cx,4
+               jmp short writehex_common
+writehex8:
+               pushad
+               mov cx,8
+writehex_common:
+.loop:         rol eax,4
+               push eax
+               and al,0Fh
+               cmp al,10
+               jae .high
+.low:          add al,'0'
+               jmp short .ischar
+.high:         add al,'A'-10
+.ischar:       call writechr
+               pop eax
+               loop .loop
+               popad
+               ret
+
 ;
 ; pollchar: check if we have an input character pending (ZF = 0)
 ;
@@ -2371,13 +2416,17 @@ kaboom2:
 ;              similar to the C library getc routine.  Only one simultaneous
 ;              use is supported.  Note: "open" trashes the trackbuf.
 ;
-;              open:   Input:  mangled filename in DS:DI
+;              open:   Input:  filename in DS:DI
 ;                      Output: ZF set on file not found or zero length
 ;
 ;              getc:   Output: CF set on end of file
 ;                              Character loaded in AL
 ;
 open:
+               mov di,pxe_tftp_open_pkt
+               mov bx,0020h            ; PXENV_TFTP_OPEN
+               call far [PXENVEntry]
+
                call searchdir
                jz open_return
                pushf
@@ -2415,7 +2464,7 @@ getc_oksize:      sub [FClust],cx         ; Reduce remaining clusters
                push es                 ; ES may be != DS, save old ES
                push ds                 ; Trackbuf is in DS, not ES
                pop es
-               call getfssec           ; Load a trackbuf full of data
+;              call getfssec           ; Load a trackbuf full of data
                mov [FNextClust],si     ; Store new next pointer
                pop es                  ; Restore ES
                pop si                  ; SI -> newly loaded data
@@ -2684,89 +2733,6 @@ gl_ret:          pushf                   ; We want the last char to be space!
 gl_xret:       popf
                ret
 
-
-%ifdef debug           ; This code for debugging only
-;
-; dumpregs:    Dumps the contents of all registers
-;
-                assume ds:_text, es:NOTHING, fs:NOTHING, gs:NOTHING
-dumpregs       proc near               ; When calling, IP is on stack
-               pushf                   ; Store flags
-               pusha
-               push ds
-               push es
-               push fs
-               push gs
-               push cs                 ; Set DS <- CS
-               pop ds
-               cld                     ; Clear direction flag
-               mov si,offset crlf_msg
-               call cwritestr
-               mov bx,sp
-               add bx,byte 26
-               mov si,offset regnames
-               mov cx,2                ; 2*7 registers to dump
-dump_line:     push cx
-               mov cx,7                ; 7 registers per line
-dump_reg:      push cx
-               mov cx,4                ; 4 characters/register name
-wr_reg_name:   lodsb
-               call writechr
-               loop wr_reg_name
-               mov ax,ss:[bx]
-               dec bx
-               dec bx
-               call writehex
-               pop cx
-               loop dump_reg
-               mov al,0Dh              ; <CR>
-               call writechr
-               mov al,0Ah              ; <LF>
-               call writechr
-               pop cx
-               loop dump_line
-               pop gs
-               pop fs
-               pop es
-               pop ds
-               popa                    ; Restore the remainder
-               popf                    ; Restore flags
-               ret
-dumpregs       endp
-
-regnames       db ' IP: FL: AX: CX: DX: BX: SP: BP: SI: DI: DS: ES: FS: GS:'
-
-;
-; writehex:    Writes a 16-bit hexadecimal number (in AX)
-;
-writehex       proc near
-               push bx
-               push cx
-               mov cx,4                ; 4 numbers
-write_hexdig:  xor bx,bx
-               push cx
-               mov cx,4                ; 4 bits/digit
-xfer_digit:    shl ax,1
-               rcl bx,1
-               loop xfer_digit
-               push ax
-               mov ax,bx
-               or al,'0'
-               cmp al,'9'
-               jna ok_digit
-               add al,'A'-'0'-10
-ok_digit:      call writechr
-               pop ax
-               pop cx
-               loop write_hexdig
-               pop cx
-               pop bx
-               ret
-writehex       endp
-
-debug_magic    dw 0D00Dh
-
-%endif ; debug
 ;
 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
 ;             to by ES:DI; ends on encountering any whitespace
@@ -2878,6 +2844,29 @@ lc_1:           cmp al,lcase_low
 lc_ret:         ret
 
 ;
+; pxe_thunk
+;
+; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
+; calling convention (using the stack.)
+;
+; This is called as a far routine so that we can just stick it into
+; the PXENVEntry variable.
+;
+pxe_thunk:     push es
+               push di
+               push bx
+               call far [PXEEntry]
+               add sp,byte 6
+               cmp ax,byte 1
+               cmc                             ; Set CF unless ax == 0
+               retf
+
+; ----------------------------------------------------------------------------------
+;  Begin data section
+; ----------------------------------------------------------------------------------
+
+
+;
 ; Lower-case table for codepage 865
 ;
 lcase_low       equ 128
@@ -2926,7 +2915,12 @@ err_bootfailed   db 0Dh, 0Ah, 'Boot failed: please change disks and press '
                db 'a key to continue.', 0Dh, 0Ah, 0
 bailmsg                equ err_bootfailed
 err_nopxe      db 'Cannot find !PXE structure, I want my mommy!' ,0Dh, 0Ah, 0
+err_pxefailed  db 'PXE API call failed, error ', 0
 found_pxenv    db 'Found PXENV+ structure', 0Dh, 0Ah, 0
+using_pxenv_msg db 'PXE API 2.0 detected, using PXENV+ structure', 0Dh, 0Ah, 0
+apiver_str     db 'PXE API version is ',0
+pxeentry_msg   db 'PXE entry point found (we hope) at ', 0
+myipaddr_msg   db 'My IP address seems to be ',0
 loading_msg     db 'Loading ', 0
 dotdot_msg      db '.'
 dot_msg         db '.', 0
@@ -2934,8 +2928,9 @@ aborted_msg       db ' aborted.'                  ; Fall through to crlf_msg!
 crlf_msg       db 0Dh, 0Ah, 0
 crff_msg       db 0Dh, 0Ch, 0
 syslinux_cfg   db 'SYSLINUXCFG'
+default_str    db 'default', 0
+default_len    equ ($-default_str)
 pxelinux_banner        db 0Dh, 0Ah, 'PXELINUX ', version_str, ' ', date, ' ', 0
-               db 0Dh, 0Ah, 1Ah        ; EOF if we "type" this in DOS
 
 ;
 ; Command line options we'd like to take a look at
@@ -2946,7 +2941,8 @@ initrd_cmd_len    equ 7
 ;
 ; Config file keyword table
 ;
-               align 2
+               align 2, db 0
+
 keywd_table    db 'ap' ; append
                db 'de' ; default
                db 'ti' ; timeout
@@ -2975,6 +2971,7 @@ keywd_table       db 'ap' ; append
 ; extension, needed for error messages.  The exten_table is shifted so
 ; the table is 1-based; this is because a "loop" cx is used as index.
 ;
+               align 4, db 0
 exten_table:
 OrigKernelExt: dd 0                    ; Original extension
                db 'COM',0              ; COMBOOT (same as DOS)
@@ -2983,6 +2980,31 @@ OrigKernelExt:   dd 0                    ; Original extension
                db 'CBT',0              ; COMBOOT (specific)
 
 exten_count    equ (($-exten_table) >> 2) - 1  ; Number of alternates
+
+;
+; PXENV entry point.  If we use the !PXE API, this will point to a thunk
+; function that converts to the !PXE calling convention.
+;
+PXENVEntry     dw pxe_thunk,0
+
+;
+; PXE query packets partially filled in
+;
+pxe_bootp_query_pkt:
+.status:       dw 0                    ; Status
+.packettype:   dw 2                    ; DHCPACK packet
+.buffersize:   dw bootp_size           ; Packet size
+.buffer:       dw bootp, 0             ; seg:off of buffer
+.bufferlimit:  dw 0                    ; Unused
+
+pxe_tftp_open_pkt:
+.status:       dw 0                    ; Status
+.sip           dd 0                    ; Server IP
+.gip           dd 0                    ; Gateway IP
+.filename      times 128 db 0          ; Input filename
+.tftpport      dw 69                   ; TFTP port
+.packetsize    dw 1024                 ; Packet size
+
 ;
 ; Misc initialized (data) variables
 ;