Core:PXELINUX: try to merge pxelinux
authorLiu Aleaxander <Aleaxander@gmail.com>
Thu, 6 Aug 2009 07:17:48 +0000 (15:17 +0800)
committerLiu Aleaxander <Aleaxander@gmail.com>
Thu, 6 Aug 2009 07:17:48 +0000 (15:17 +0800)
the others Syslinux derivative disabled for now

16 files changed:
com32/include/com32.h
core/Makefile
core/bios.inc
core/cache.c
core/dhcp_option.c [new file with mode: 0644]
core/disk.c [deleted file]
core/diskio.c
core/dnsresolv.inc
core/extern.inc
core/fs.c
core/include/disk.h
core/include/fs.h
core/include/pxe.h [new file with mode: 0644]
core/parsecmd.inc
core/pxe.c [new file with mode: 0644]
core/pxelinux.asm

index c003f7c..50485ae 100644 (file)
@@ -153,21 +153,27 @@ static inline void *MK_PTR(uint16_t __seg, uint16_t __offs)
 /* Some tools to handle 16:16 far pointers in memory */
 
 struct __far_ptr {
-    uint32_t __ptr;
+    union {
+       uint32_t ptr;
+       struct {
+           uint16_t offs, seg;
+       };
+    };
 } __attribute__ ((packed));
 
 typedef struct __far_ptr far_ptr_t;
 
 static inline void *GET_PTR(far_ptr_t __fptr)
 {
-    return MK_PTR(__fptr.__ptr >> 16, __fptr.__ptr);
+    return MK_PTR(__fptr.seg, __fptr.offs);
 }
 
 static inline far_ptr_t FAR_PTR(void *__ptr)
 {
     far_ptr_t __fptr;
 
-    __fptr.__ptr = (SEG(__ptr) << 16) + OFFS(__ptr);
+    __fptr.offs = OFFS(__ptr);
+    __fptr.seg  = SEG(__ptr);
     return __fptr;
 }
 
index 1cb1170..b690ef3 100644 (file)
@@ -32,10 +32,10 @@ INCLUDES = -I./include -I$(com32)/include
 CODEPAGE = cp865
 
 # The targets to build in this directory...
-BTARGET  = kwdhash.gen \
-          extlinux.bin extlinux.bss extlinux.sys \
+BTARGET  = kwdhash.gen pxelinux.0
+          #extlinux.bin extlinux.bss extlinux.sys \
           ldlinux.bss ldlinux.sys ldlinux.bin \
-          isolinux.bin isolinux-debug.bin
+          pxelinux.0 isolinux.bin isolinux-debug.bin
 
 # All primary source files for the main syslinux files
 NASMSRC         := $(wildcard *.asm)
index 987a216..4c6c5b5 100644 (file)
@@ -18,6 +18,7 @@
 
 %ifndef _BIOS_INC
 %define _BIOS_INC
+                global BIOS_fbm, BIOS_timer
 
                absolute 4*1Eh          ; In the interrupt table
 fdctab         equ $
index b0fbee7..4be99bb 100644 (file)
@@ -73,11 +73,11 @@ void cache_init(struct device *dev, int block_size_shift)
  */
 struct cache_struct* get_cache_block(struct device *dev, block_t block)
 {
+    struct cache_struct *head = (struct cache_struct *)dev->cache_head;
+    struct cache_struct *last = head->prev;
     /* let's find it from the end, 'cause the endest is the freshest */
-    struct cache_struct *cs = dev->cache_head->prev;
-    struct cache_struct *head,  *last;
+    struct cache_struct *cs = head->prev;
     int i;
-
     static int total_read;
     static int missed;
         
@@ -104,7 +104,7 @@ struct cache_struct* get_cache_block(struct device *dev, block_t block)
     /* missed, so we need to load it */
     if (i == dev->cache_entries) {        
         /* store it at the head of real cache */
-        cs = dev->cache_head->next;        
+        cs = head->next;        
         cs->block = block;
         getoneblk(cs->data, block, dev->cache_block_size);
 
@@ -116,9 +116,6 @@ struct cache_struct* get_cache_block(struct device *dev, block_t block)
     cs->next->prev = cs->prev;    
     
     /* add to just before head node */
-    last = dev->cache_head->prev;
-    head = dev->cache_head;
-    
     last->next = cs;
     cs->prev = last;
     head->prev = cs;
diff --git a/core/dhcp_option.c b/core/dhcp_option.c
new file mode 100644 (file)
index 0000000..f9ff4f2
--- /dev/null
@@ -0,0 +1,259 @@
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <pxe.h>
+#include <sys/cpu.h>
+
+void subnet_mask(void *data, int opt_len)
+{
+    if (opt_len != 4)
+       return;
+    Netmask = *(uint32_t *)data;
+}
+
+void router(void *data, int opt_len)
+{
+    if (opt_len != 4)
+       return;
+    Gateway = *(uint32_t *)data;
+}
+
+void dns_servers(void *data, int opt_len)
+{
+    int num = opt_len >> 2;
+    int i;
+
+    if (num > DNS_MAX_SERVERS)
+        num = DNS_MAX_SERVERS;
+
+    for (i = 0; i < num; i++) {
+        DNSServers[i] = *(uint32_t *)data;
+        data += 4;
+    }
+    
+    /* NOT SURE FOR NOW */
+    LastDNSServer = OFFS_WRT(&DNSServers[num - 1], 0);
+}
+
+void local_domain(void *data, int opt_len)
+{
+    com32sys_t regs;
+    char *p = (char *)data + opt_len;
+    char end = *p;
+
+    memset(&regs, 0, sizeof regs);
+    *p = '\0';   /* Zero-terminate option */
+    regs.esi.w[0] = OFFS_WRT(data, 0);
+    regs.edi.w[0] = OFFS_WRT(LocalDomain, 0);
+    call16(dns_mangle, &regs, NULL);
+    *p = end;    /* Resotre ending byte */
+}
+
+void vendor_encaps(void *data, int opt_len)
+{
+    /* Only recongnize PXELINUX options */
+    parse_dhcp_options(data, opt_len, 208);
+}
+
+void option_overload(void *data, int opt_len)
+{
+    if (opt_len != 1)
+       return;
+    OverLoad = *(uint8_t *)data;
+}
+
+
+void server(void *data, int opt_len)
+{
+    uint32_t ip;
+
+    if (opt_len != 4)
+       return;
+    
+    if (ServerIP)
+        return;
+    
+    ip = *(uint32_t *)data;
+    if (ip_ok(ip))
+        ServerIP = ip;
+}
+
+void client_identifier(void *data, int opt_len)
+{
+    if (opt_len > MAC_MAX || opt_len < 2 ||
+        MACLen != (opt_len >> 8) || 
+        *(uint8_t *)data != MACType)
+        return;
+
+    opt_len --;
+    MACLen = opt_len & 0xff;
+    memcpy(MAC, data+1, opt_len);
+    MAC[opt_len] = 0;
+}
+
+void bootfile_name(void *data, int opt_len)
+{
+    strncpy(BootFile, data, opt_len);
+    BootFile[opt_len] = 0;
+}
+   
+void uuid_client_identifier(void *data, int opt_len)
+{
+    int type = *(uint8_t *)data;
+    if (opt_len != 17 ||
+        (type | HaveUUID))
+        return;
+
+    HaveUUID = 1;
+    UUIDType = type;
+    memcpy(UUID, data+1, 16);
+    UUID[16] = 0;
+}
+
+void pxelinux_configfile(void *data, int opt_len)
+{
+    DHCPMagic |= 2;
+    strncpy(ConfigName, data, opt_len);
+    ConfigName[opt_len] = 0;
+}
+
+void pxelinux_pathprefix(void *data,int opt_len)
+{
+    DHCPMagic |= 4;
+    strncpy(PathPrefix, data, opt_len);
+    PathPrefix[opt_len] = 0;
+}
+
+void pxelinux_reboottime(void *data, int opt_len)
+{
+    if ((opt_len && 0xff) != 4)
+        return ;
+    
+    RebootTime = ntohl(*(uint32_t *)data);
+    DHCPMagic |= 8;     /* Got reboot time */
+}
+
+
+struct dhcp_options {
+    int opt_num;
+    void (*fun) (void *, int);
+};
+
+struct dhcp_options dhcp_opts[] = { 
+    {1,   subnet_mask},
+    {3,   router},
+    {6,   dns_servers},
+    {15,  local_domain},
+    {43,  vendor_encaps},
+    {52,  option_overload},
+    {54,  server},
+    {61,  client_identifier},
+    {67,  bootfile_name},
+    {97,  uuid_client_identifier},
+    {209, pxelinux_configfile},
+    {210, pxelinux_pathprefix},
+    {211, pxelinux_reboottime}
+};
+
+/*
+ * Parse a sequence of DHCP options, pointed to by _option_; 
+ * -- some DHCP servers leave option fields unterminated
+ * in violation of the spec.
+ *
+ * filter  contains the minimum value for the option to recognize
+ * -- this is used to restrict parsing to PXELINUX-specific options only.
+ */  
+void parse_dhcp_options(void *option, int size, int filter)
+{
+    uint8_t opt_num;
+    uint8_t opt_len;
+    uint8_t opt_filter = filter == 208 ? 208 : 0;
+    int opt_entries = sizeof(dhcp_opts) / sizeof(dhcp_opts[0]);
+    int i = 0;
+    char *p = option;
+    struct dhcp_options *opt;
+    
+    if (opt_filter)
+        printf("***NOTE!:*** we hit a pxelinux-specific options\n");
+    
+    while (size --) {
+        opt_num = *p++;
+
+        if (!size)
+            break;
+        if (opt_num == 0)
+            continue;
+        if (opt_num == 0xff)
+            break;
+        
+        /* Anything else will have a lenght filed */
+        opt_len = *p++; /* c  <- option lenght */
+        size = size - opt_len - 1;
+        if (size < 0)
+            break;
+        if (opt_num < opt_filter) {       /* Is the option value valid */
+            option += opt_len;   /* Try next */
+            continue;
+        }
+
+        opt = dhcp_opts;
+        for (i = 0; i < opt_entries; i++) {
+            if (opt_num == opt->opt_num) {
+                opt->fun(p, opt_len);
+                break;
+            }            
+            opt ++;
+        }
+        
+        /* parse next */
+        p += opt_len;
+    }
+}
+/*
+ *
+ ;
+ ; parse_dhcp
+ ;
+ ; Parse a DHCP packet.  This includes dealing with "overloaded"
+ ; option fields (see RFC 2132, section 9.3)
+ ;
+ ; This should fill in the following global variables, if the
+ ; information is present:
+ ;
+ ; MyIP                - client IP address
+ ; ServerIP    - boot server IP address
+ ; Netmask     - network mask
+ ; Gateway     - default gateway router IP
+ ; BootFile    - boot file name
+ ; DNSServers  - DNS server IPs
+ ; LocalDomain - Local domain name
+ ; MACLen, MAC - Client identifier, if MACLen == 0
+ ;
+ ; This assumes the DHCP packet is in "trackbuf".
+ ;
+*/
+void parse_dhcp(int pkt_len)
+{
+    struct bootp_t *dhcp = (struct bootp_t *)trackbuf;
+    int opt_len;
+
+    OverLoad = 0;
+    if (ip_ok(dhcp->yip))
+        MyIP = dhcp->yip;
+    
+    if (ip_ok(dhcp->sip))
+        ServerIP = dhcp->sip;
+    
+    opt_len = (char *)dhcp + pkt_len - (char *)&dhcp->options;
+    if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC)) 
+        parse_dhcp_options(&dhcp->options, opt_len, 0);
+
+    if (OverLoad & 1) 
+        parse_dhcp_options(&dhcp->bootfile, 128, 0);
+    else if (dhcp->bootfile[0]) 
+            strcpy(BootFile, dhcp->bootfile);
+    
+    if (OverLoad & 2) 
+        parse_dhcp_options(dhcp->sname, 64, 0);
+}  
diff --git a/core/disk.c b/core/disk.c
deleted file mode 100644 (file)
index 69a9c0b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include "core.h"
-#include "disk.h"
-
-void read_sectors(char *buf, sector_t sector_num, int sectors)
-{
-    com32sys_t regs;
-    //static __lowmem char low_buf[65536]; 
-    /* for safe, we use buf + (sectors << SECTOR_SHIFT) here */
-    int high_addr = (buf + (sectors << SECTOR_SHIFT)) > (char *)0x100000;
-        
-    memset(&regs, 0, sizeof regs);
-    regs.eax.l = sector_num;
-    regs.ebp.l = sectors;
-    
-    if (high_addr) {
-        regs.es = SEG(core_xfer_buf);
-        regs.ebx.w[0] = OFFS(core_xfer_buf);
-    } else {
-        regs.es = SEG(buf);
-        regs.ebx.w[0] = OFFS(core_xfer_buf);
-    }
-
-    call16(getlinsec, &regs, NULL);
-
-    if (high_addr)
-        memcpy(buf, core_xfer_buf, sectors << SECTOR_SHIFT);
-}
-
-
-void getoneblk(char *buf, block_t block, int block_size)
-{
-    int sec_per_block = block_size >> SECTOR_SHIFT;
-        
-    read_sectors(buf, block * sec_per_block, sec_per_block);
-}
-
-
index bf22220..274157b 100644 (file)
@@ -303,3 +303,43 @@ struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start,
 #endif
     return &disk;
 }
+
+
+/* 
+ * initialize the device structure 
+ */
+struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
+                            uint16_t bsHeads, uint16_t bsSecPerTrack)
+{
+    static struct device dev;
+
+    dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack);
+        
+    /* for now, isolinux doesn't use cache */
+    if (!cdrom) {
+        /*
+         * FIX!! I can't use __lowmem here, 'cause it will cause the error:
+         * "auxseg/lowmem region collides with xfer_buf_seg".
+         *
+         * static __lowmem char cache_buf[65536];
+         */
+        dev.cache_data = core_cache_buf;
+        dev.cache_size = sizeof core_cache_buf;
+    } else 
+        dev.cache_data = NULL;
+
+    return &dev;
+}
+
+
+/* debug function */
+void dump_dev(struct device *dev)
+{
+    printf("device type:%s\n", dev->disk->type ? "EDD" : "CHS");
+    printf("drive number: 0x%x\n", dev->disk->disk_number);
+    printf("cache_data: %p\n", dev->cache_data);
+    printf("cache_head: %p\n", dev->cache_head);
+    printf("cache_block_size: %d\n", dev->cache_block_size);
+    printf("cache_entries: %d\n", dev->cache_entries);
+    printf("cache_size: %d\n", dev->cache_size);
+}
index 7b63237..9733282 100644 (file)
@@ -33,6 +33,7 @@ DNS_MAX_SERVERS equ 4                 ; Max no of DNS servers
 ;
 ; On return, DX contains the number of dots encountered.
 ;
+                global dns_mangle
 dns_mangle:
                push ax
                push bx
@@ -151,6 +152,7 @@ dns_skiplabel:
                endstruc
 
                section .bss16
+                global LocalDomain, DNSServers
                alignb 2
 DNSSendBuf     resb DNS_MAX_PACKET
 DNSRecvBuf     resb DNS_MAX_PACKET
@@ -176,6 +178,7 @@ pxe_udp_read_pkt_dns:
 .buffersize:   dw DNS_MAX_PACKET       ; Max packet size
 .buffer:       dw DNSRecvBuf, 0        ; off, seg of buffer
 
+                global LastDNSServer
 LastDNSServer  dw DNSServers
 
 ; Actual resolver function
@@ -186,6 +189,7 @@ LastDNSServer       dw DNSServers
 ; No segment assumptions permitted.
 ;
                section .text16
+                global dns_resolv
 dns_resolv:
                push ds
                push es
index 2e6dcd0..a820c9a 100644 (file)
@@ -18,4 +18,7 @@
         ; fat.c
         extern alloc_fill_dir, readdir
 
+        ; pxe.c
+        extern gendotquad
+
 %endif ; EXTERN_INC
index 165d07e..7dc3b2a 100644 (file)
--- a/core/fs.c
+++ b/core/fs.c
@@ -2,7 +2,7 @@
 #include <stdbool.h>
 #include <string.h>
 #include "fs.h"
-#include "cache.h"
+//#include "cache.h"
 
 
 /* The this fs pointer */
@@ -75,45 +75,6 @@ void searchdir(com32sys_t *regs)
 }
 
 
-/* 
- * initialize the device structure 
- */
-struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
-                            uint16_t bsHeads, uint16_t bsSecPerTrack)
-{
-    static struct device dev;
-
-    dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack);
-        
-    /* for now, isolinux doesn't use cache */
-    if (!cdrom) {
-        /*
-         * FIX!! I can't use __lowmem here, 'cause it will cause the error:
-         * "auxseg/lowmem region collides with xfer_buf_seg".
-         *
-         * static __lowmem char cache_buf[65536];
-         */
-        dev.cache_data = core_cache_buf;
-        dev.cache_size = sizeof core_cache_buf;
-    } else 
-        dev.cache_data = NULL;
-
-    return &dev;
-}
-
-
-/* debug function */
-void dump_dev(struct device *dev)
-{
-    printf("device type:%s\n", dev->disk->type ? "EDD" : "CHS");
-    printf("drive number: 0x%x\n", dev->disk->disk_number);
-    printf("cache_data: %p\n", dev->cache_data);
-    printf("cache_head: %p\n", dev->cache_head);
-    printf("cache_block_size: %d\n", dev->cache_block_size);
-    printf("cache_entries: %d\n", dev->cache_entries);
-    printf("cache_size: %d\n", dev->cache_size);
-}
-
 /*
  * it will do:
  *    set up the vfs fs structure;
@@ -130,7 +91,10 @@ void fs_init(com32sys_t *regs)
     /* set up the fs stucture */    
     fs.fs_name = ops->fs_name;
     fs.fs_ops = ops;
-    fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l,
+    if (1)//! strcmp(fs.fs_name, "pxe"))
+        fs.fs_dev = NULL;
+    else 
+        fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l, \
                             regs->esi.w[0], regs->edi.w[0]);
     this_fs = &fs;
 
@@ -138,6 +102,6 @@ void fs_init(com32sys_t *regs)
     blk_shift = fs.fs_ops->fs_init(&fs);    
 
     /* initialize the cache */
-    if (fs.fs_dev->cache_data)
-        cache_init(fs.fs_dev, blk_shift);
+    //if (fs.fs_dev && fs.fs_dev->cache_data)
+        // cache_init(fs.fs_dev, blk_shift);
 }
index 386f4f7..0d8702d 100644 (file)
@@ -35,5 +35,6 @@ extern void getoneblk(char *, block_t, int);
 
 /* diskio.c */
 struct disk *disk_init(uint8_t, bool, sector_t, uint16_t, uint16_t);
+struct device *device_init(uint8_t, bool, sector_t, uint16_t, uint16_t);                           
 
 #endif /* DISK_H */
index b5128d8..163808e 100644 (file)
@@ -44,7 +44,7 @@ struct device {
 
     /* the cache stuff */
     char* cache_data;
-    struct cache_struct* cache_head;
+    void* cache_head;
     uint16_t cache_block_size;
     uint16_t cache_entries;
     uint32_t cache_size;
diff --git a/core/include/pxe.h b/core/include/pxe.h
new file mode 100644 (file)
index 0000000..b9c71df
--- /dev/null
@@ -0,0 +1,370 @@
+
+/**
+* -----------------------------------------------------------------------
+*
+*   Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+*
+*   This program is free software; you can redistribute it and/or modify
+*   it under the terms of the GNU General Public License as published by
+*   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+*   Boston MA 02111-1307, USA; either version 2 of the License, or
+*   (at your option) any later version; incorporated herein by reference.
+*
+* -----------------------------------------------------------------------
+
+*
+* pxe.inc
+*
+* PXE opcodes
+*
+*/
+#ifndef PXE_H
+#define PXE_H
+
+
+#define PXENV_TFTP_OPEN                                         0x0020
+#define PXENV_TFTP_CLOSE                                0x0021
+#define PXENV_TFTP_READ                                         0x0022
+#define PXENV_TFTP_READ_FILE                            0x0023
+#define PXENV_TFTP_READ_FILE_PMODE                      0x0024
+#define PXENV_TFTP_GET_FSIZE                            0x0025
+
+#define PXENV_UDP_OPEN                                  0x0030
+#define PXENV_UDP_CLOSE                                         0x0031
+#define PXENV_UDP_READ                                  0x0032
+#define PXENV_UDP_WRITE                                         0x0033
+
+#define PXENV_START_UNDI                                0x0000
+#define PXENV_UNDI_STARTUP                              0x0001
+#define PXENV_UNDI_CLEANUP                              0x0002
+#define PXENV_UNDI_INITIALIZE                           0x0003
+#define PXENV_UNDI_RESET_NIC                            0x0004
+#define PXENV_UNDI_SHUTDOWN                             0x0005
+#define PXENV_UNDI_OPEN                                         0x0006
+#define PXENV_UNDI_CLOSE                                0x0007
+#define PXENV_UNDI_TRANSMIT                             0x0008
+#define PXENV_UNDI_SET_MCAST_ADDR                       0x0009
+#define PXENV_UNDI_SET_STATION_ADDR                     0x000A
+#define PXENV_UNDI_SET_PACKET_FILTER                    0x000B
+#define PXENV_UNDI_GET_INFORMATION                      0x000C
+#define PXENV_UNDI_GET_STATISTICS                       0x000D
+#define PXENV_UNDI_CLEAR_STATISTICS                     0x000E
+#define PXENV_UNDI_INITIATE_DIAGS                       0x000F
+#define PXENV_UNDI_FORCE_INTERRUPT                      0x0010
+#define PXENV_UNDI_GET_MCAST_ADDR                       0x0011
+#define PXENV_UNDI_GET_NIC_TYPE                                 0x0012
+#define PXENV_UNDI_GET_IFACE_INFO                       0x0013
+#define PXENV_UNDI_ISR                                  0x0014
+#define PXENV_STOP_UNDI                                         0x0015
+#define PXENV_UNDI_GET_STATE                            0x0015
+
+#define PXENV_UNLOAD_STACK                              0x0070
+#define PXENV_GET_CACHED_INFO                           0x0071
+#define PXENV_RESTART_DHCP                              0x0072
+#define PXENV_RESTART_TFTP                              0x0073
+#define PXENV_MODE_SWITCH                               0x0074
+#define PXENV_START_BASE                                0x0075
+#define PXENV_STOP_BASE                                         0x0076
+
+/* gPXE extensions... */
+#define PXENV_FILE_OPEN                                         0x00e0
+#define PXENV_FILE_CLOSE                                0x00e1
+#define PXENV_FILE_SELECT                               0x00e2
+#define PXENV_FILE_READ                                         0x00e3
+#define PXENV_GET_FILE_SIZE                             0x00e4
+#define PXENV_FILE_EXEC                                         0x00e5
+#define PXENV_FILE_API_CHECK                            0x00e6
+
+/* Exit codes */
+#define PXENV_EXIT_SUCCESS                              0x0000
+#define PXENV_EXIT_FAILURE                              0x0001
+
+/* Status codes */
+#define PXENV_STATUS_SUCCESS                            0x00
+#define PXENV_STATUS_FAILURE                            0x01
+#define PXENV_STATUS_BAD_FUNC                           0x02
+#define PXENV_STATUS_UNSUPPORTED                        0x03
+#define PXENV_STATUS_KEEP_UNDI                          0x04
+#define PXENV_STATUS_KEEP_ALL                           0x05
+#define PXENV_STATUS_OUT_OF_RESOURCES                   0x06
+#define PXENV_STATUS_ARP_TIMEOUT                        0x11
+#define PXENV_STATUS_UDP_CLOSED                                 0x18
+#define PXENV_STATUS_UDP_OPEN                           0x19
+#define PXENV_STATUS_TFTP_CLOSED                        0x1a
+#define PXENV_STATUS_TFTP_OPEN                          0x1b
+#define PXENV_STATUS_MCOPY_PROBLEM                      0x20
+#define PXENV_STATUS_BIS_INTEGRITY_FAILURE              0x21
+#define PXENV_STATUS_BIS_VALIDATE_FAILURE               0x22
+#define PXENV_STATUS_BIS_INIT_FAILURE                   0x23
+#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE               0x24
+#define PXENV_STATUS_BIS_GBOA_FAILURE                   0x25
+#define PXENV_STATUS_BIS_FREE_FAILURE                   0x26
+#define PXENV_STATUS_BIS_GSI_FAILURE                    0x27
+#define PXENV_STATUS_BIS_BAD_CKSUM                      0x28
+#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS            0x30
+#define PXENV_STATUS_TFTP_OPEN_TIMEOUT                  0x32
+
+#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE                0x33
+#define PXENV_STATUS_TFTP_READ_TIMEOUT                  0x35
+#define PXENV_STATUS_TFTP_ERROR_OPCODE                  0x36
+#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION        0x38
+#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION   0x39
+#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES             0x3a
+#define PXENV_STATUS_TFTP_FILE_NOT_FOUND                0x3b
+#define PXENV_STATUS_TFTP_ACCESS_VIOLATION              0x3c
+#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS              0x3d
+#define PXENV_STATUS_TFTP_NO_FILESIZE                   0x3e
+#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE           0x3f
+#define PXENV_STATUS_DHCP_TIMEOUT                       0x51
+#define PXENV_STATUS_DHCP_NO_IP_ADDRESS                         0x52
+#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME              0x53
+#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS                0x54
+#define PXENV_STATUS_UNDI_INVALID_FUNCTION              0x60
+#define PXENV_STATUS_UNDI_MEDIATEST_FAILED              0x61
+#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST     0x62
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC                 0x63
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY                 0x64
+#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA       0x65
+#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA                 0x66
+#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS               0x67
+#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM           0x68
+#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR             0x69
+#define PXENV_STATUS_UNDI_INVALID_STATE                         0x6a
+#define PXENV_STATUS_UNDI_TRANSMIT_ERROR                0x6b
+#define PXENV_STATUS_UNDI_INVALID_PARAMETER             0x6c
+#define PXENV_STATUS_BSTRAP_PROMPT_MENU                         0x74
+#define PXENV_STATUS_BSTRAP_MCAST_ADDR                  0x76
+#define PXENV_STATUS_BSTRAP_MISSING_LIST                0x77
+#define PXENV_STATUS_BSTRAP_NO_RESPONSE                         0x78
+#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG                0x79
+#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE                 0xa0
+#define PXENV_STATUS_BINL_NO_PXE_SERVER                         0xa1
+#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE             0xa2
+#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE             0xa3
+#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED          0xb0
+#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY                 0xc0
+#define PXENV_STATUS_LOADER_NO_BC_ROMID                         0xc1
+#define PXENV_STATUS_LOADER_BAD_BC_ROMID                0xc2
+#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE        0xc3
+#define PXENV_STATUS_LOADER_NO_UNDI_ROMID               0xc4
+#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID              0xc5
+#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE       0xc6
+#define PXENV_STATUS_LOADER_NO_PXE_STRUCT               0xc8
+#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT             0xc9
+#define PXENV_STATUS_LOADER_UNDI_START                  0xca
+#define PXENV_STATUS_LOADER_BC_START                    0xcb
+
+
+
+
+/*
+ * some other defines 
+ */
+#define PKTBUF_SIZE        (65536 / MAX_OPEN)
+
+#define TFTP_BLOCKSIZE_LG2 9
+#define TFTP_BLOCKSIZE     (1 << TFTP_BLOCKSIZE_LG2)
+#define PKTBUF_SEG 0x4000
+#define DNS_MAX_SERVERS    4
+
+#define is_digit(c) (((c) >= '0') && ((c) <= '9'))
+#define htons(x)    ( ( ((x) & 0xff) << 8) + ( ((x) &0xff00) >> 8) )
+#define ntohs(x)    htons(x)
+#define htonl(x)    ( ( ((x) & 0xff) << 24)     + ( ((x) & 0xff00) << 8 ) + \
+                      ( ((x) & 0xff0000) >> 8 ) + ( ((x) & 0xff000000) >> 24) )
+#define ntohl(x)    htonl(x)
+
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ        htons(1)               // Read rest
+#define TFTP_WRQ        htons(2)               // Write rest
+#define TFTP_DATA       htons(3)               // Data packet
+#define TFTP_ACK        htons(4)               // ACK packet
+#define TFTP_ERROR      htons(5)               // ERROR packet
+#define TFTP_OACK       htons(6)               // OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF     htons(0)               // Unspecified error
+#define TFTP_ENOTFOUND  htons(1)               // File not found
+#define TFTP_EACCESS    htons(2)               // Access violation
+#define TFTP_ENOSPACE   htons(3)               // Disk full
+#define TFTP_EBADOP     htons(4)               // Invalid TFTP operation
+#define TFTP_EBADID     htons(5)               // Unknown transfer
+#define TFTP_EEXISTS    htons(6)               // File exists
+#define TFTP_ENOUSER    htons(7)               // No such user
+#define TFTP_EOPTNEG    htons(8)               // Option negotiation failure
+
+#define BOOTP_OPTION_MAGIC  htonl(0x63825363)
+
+
+/*
+ * structures 
+ */
+struct bootp_t {
+    uint8_t  opcode;        /* BOOTP/DHCP "opcode" */
+    uint8_t  hardware;      /* ARP hreadware type */
+    uint8_t  hardlen;       /* Hardware address length */
+    uint8_t  gatehops;      /* Used by forwarders */
+    uint32_t ident;         /* Transaction ID */
+    uint16_t seconds;       /* Seconds elapsed */
+    uint16_t flags;         /* Broadcast flags */
+    uint32_t cip;           /* Cient IP */
+    uint32_t yip;           /* "Your" IP */
+    uint32_t sip;           /* Next Server IP */
+    uint32_t gip;           /* Relay agent IP */
+    uint8_t  macaddr[16];   /* Client MAC address */
+    uint8_t  sname[64];     /* Server name (optional) */
+    char     bootfile[128]; /* Boot file name */
+    uint32_t option_magic;  /* Vendor option magic cookie */
+    uint8_t  options[1260]; /* Vendor options */
+} __attribute__ ((packed));
+
+struct open_file_t {
+    uint16_t tftp_localport;   /* Local port number  (0=not in us)*/
+    uint16_t tftp_remoteport;  /* Remote port number */
+    uint32_t tftp_remoteip;    /* Remote IP address */
+    uint32_t tftp_filepos;     /* bytes downloaded (includeing buffer) */
+    uint32_t tftp_filesize;    /* Total file size(*) */
+    uint32_t tftp_blksize;     /* Block size for this connection(*) */
+    uint16_t tftp_bytesleft;   /* Unclaimed data bytes */
+    uint16_t tftp_lastpkt;     /* Sequence number of last packet (NBO) */
+    uint16_t tftp_dataptr;     /* Pointer to available data */
+    uint8_t  tftp_goteof;      /* 1 if the EOF packet received */
+    uint8_t  tftp_unused[3];   /* Currently unused */
+    uint16_t tftp_pktbuf;      /* Packet buffer offset */
+} __attribute__ ((packed));
+extern char Files[];
+
+struct pxe_udp_write_pkt {
+    uint16_t status;
+    uint32_t sip;
+    uint32_t gip;
+    uint16_t lport;
+    uint16_t rport;
+    uint16_t buffersize;
+    uint16_t buffer[2];
+} __attribute__ ((packed));
+
+struct pxe_udp_read_pkt {
+    uint16_t status;
+    uint32_t sip;
+    uint32_t dip;
+    uint16_t rport;
+    uint16_t lport;
+    uint16_t buffersize;
+    uint16_t buffer[2];
+} __attribute__ ((packed));
+
+struct pxe_bootp_query_pkt {
+    uint16_t status;
+    uint16_t packettype;
+    uint16_t buffersize;
+    uint16_t buffer[2];
+    uint16_t bufferlimit;
+} __attribute__ ((packed));
+
+struct pxe_udp_open_pkt {
+    uint16_t status;
+    uint32_t sip;
+} __attribute__ ((packed));
+
+struct gpxe_file_api_check {
+    uint16_t status;
+    uint16_t size;
+    uint32_t magic;
+    uint32_t provider;
+    uint32_t apimask;
+    uint32_t flags;
+} __attribute__ ((packed));
+
+struct gpxe_file_open {
+    uint16_t status;
+    uint16_t filehandle;
+    uint16_t filename[2];
+    uint32_t reserved;
+} __attribute__ ((packed));
+
+struct gpxe_get_file_size {
+    uint16_t status;
+    uint16_t filehandle;
+    uint32_t filesize;
+} __attribute__ ((packed));
+
+struct gpxe_file_read {
+    uint16_t status;
+    uint16_t filehandle;
+    uint16_t buffersize;
+    uint16_t buffer[2];
+} __attribute__ ((packed));
+
+/*
+ * externs 
+ */
+extern uint32_t ServerIP;
+extern uint32_t MyIP;
+extern uint32_t Netmask;
+extern uint32_t Gateway;
+extern uint32_t ServerPort;
+
+#define MAC_MAX 32
+extern char MACStr[];        /* MAC address as a string */
+extern char MAC[];           /* Actual MAC address */
+extern char BOOTIFStr[];     /* Space for "BOOTIF=" */
+extern uint8_t MACLen;       /* MAC address len */
+extern uint8_t MACType;      /* MAC address type */
+
+
+extern uint8_t  DHCPMagic;
+extern uint8_t  OverLoad;
+extern uint32_t RebootTime;
+
+
+/* TFTP ACK packet */
+extern uint16_t ack_packet_buf[];
+
+extern void kaboom(void);
+extern void dns_mangle(void);
+extern char trackbuf[];
+extern char BootFile[];
+extern char PathPrefix[];
+extern char CurrentDirName[];
+extern char LocalDomain[];
+
+extern char packet_buf[];
+
+extern char IPOption[];
+extern char DotQuadBuf[];
+
+
+extern uint32_t DNSServers[];
+extern uint16_t LastDNSServer;
+
+extern char ConfigName[];
+
+extern uint16_t RealBaseMem;
+extern uint16_t APIVer;
+extern far_ptr_t PXEEntry;
+
+extern far_ptr_t InitStack;
+
+extern int HaveUUID;
+extern uint8_t UUIDType;
+extern char UUID[];
+
+extern volatile uint16_t BIOS_timer;
+
+
+/*
+ * functions
+ */
+int ip_ok(uint32_t);
+void parse_dhcp(int);
+void parse_dhcp_options(void *, int, int);
+
+
+
+
+#endif /* pxe.h */
index 94627b8..7e0ac5c 100644 (file)
@@ -111,6 +111,7 @@ err_badcfg      db 'Unknown keyword in configuration file: ',0
 err_noparm      db 'Missing parameter in configuration file. Keyword: ',0
 
                section .uibss
+                global KernelName
                alignb 4
 vk_size                equ (vk_end + 3) & ~3
 VKernelBuf:    resb vk_size            ; "Current" vkernel
diff --git a/core/pxe.c b/core/pxe.c
new file mode 100644 (file)
index 0000000..1a61d96
--- /dev/null
@@ -0,0 +1,1551 @@
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <pxe.h>
+#include <sys/cpu.h>
+
+#define MAX_OPEN_LG2       5
+#define MAX_OPEN           (1 << MAX_OPEN_LG2)
+#define FILENAME_MAX_LG2   7
+#define FILENAME_MAX       (1 << FILENAME_MAX_LG2)
+
+#define MAX(a,b)           (a > b ? a : b)
+
+#define GPXE 1
+#define USE_PXE_PROVIDED_STACK 0
+
+char *err_nopxe     = "No !PXE or PXENV+ API found; we're dead...\n";
+char *err_pxefailed = "PXE API call failed, error ";
+char *err_udpinit   = "Failed to initialize UDP stack\n";
+char *err_noconfig  = "Unable to locate configuration file\n";
+char *err_damage    = "TFTP server sent an incomprehesible reply\n";
+
+char *tftpprefix_msg = "TFTP prefix: ";
+char *get_packet_msg = "Getting cached packet ";
+
+uint16_t NextSocket = 49152;
+
+int has_gpxe;
+int HaveUUID = 0;
+uint8_t UUIDType;
+uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
+
+uint16_t StructPtr[2];
+
+static const uint8_t TimeoutTable[] = {
+    2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44, 53, 64, 77,
+    92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
+};
+
+char *mode = "octet";
+char *tsize_str = "tsize";
+int tsize_len = 6; /* We should include the final null here */
+
+char *blksize_str = "blksize";
+int blksize_len = 8;
+
+char *asciidec = "1408";
+
+
+
+/*
+ * Allocate a local UDP port structure.
+ * return the socket pointer if success, or null if failure
+ *    
+ */
+struct open_file_t* allocate_socket()
+{
+    extern uint16_t NextSocket;
+    uint16_t i = MAX_OPEN;
+    struct open_file_t *socket = (struct open_file_t *)&Files;
+    
+    for (; i > 0; i--) {
+        if (*(uint16_t*)socket == 0)
+            break;
+        socket ++;
+    }
+    
+    /* Not found */
+    if (i == 0) 
+        return NULL;
+    
+    /*
+     * Allocate a socket number. Socket numbers are made guaranteed unique 
+     * by including the socket slot number(inverted, because we use the loop
+     * counter cx; add a counter value to keep the numbers from being likely
+     * to get immediately reused.
+     *
+     * The NextSocket variable also contains the top two bits set. This 
+     * generates a value in the range 49152 to 57343.
+     *
+     */
+    i--;
+    NextSocket = ((NextSocket + 1) & ((1  << (13-MAX_OPEN_LG2))-1)) | 0xc000;
+    i <<= 13-MAX_OPEN_LG2;
+    i += NextSocket;
+    i = ntohs(i)       ;    /* convet to network byte order, little to big */
+    *(uint16_t*)socket = i; /* socket in use */
+    
+    return socket;
+}
+
+/*
+ * free socket, socket in SI; return SI = 0, ZF = 1 for convenience
+ */
+void free_socket(struct open_file_t *file)
+{
+    /* tftp_pktbuf is not cleared */
+    memset(file, 0, sizeof(struct open_file_t) - 2);
+}
+
+/**
+ * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal 
+ *
+ * @param: dst, output buffer
+ * @param: src, input buffer
+ * @param: count, number of bytes
+ *
+ */
+void lchexbytes(char *dst, const void *src, int count)
+{
+    uint8_t half;
+    uint8_t c;
+    const uint8_t *s = src;
+    
+    for(; count > 0; count--) {
+        c = *s++;
+        half   = ((c >> 4) & 0x0f) + '0';
+        *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
+        
+        half   = (c & 0x0f) + '0';
+        *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
+    }
+}
+
+/**
+ * just like the lchexbytes, except to upper-case
+ *
+ */
+void uchexbytes(char *dst, const void *src, int count)
+{
+    uint8_t half;
+    uint8_t c;
+    const uint8_t *s = src;
+        
+    for(; count > 0; count--) {
+        c = *s++;
+        half   = ((c >> 4) & 0x0f) + '0';
+        *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
+        
+        half   = (c & 0x0f) + '0';
+        *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
+    }
+}
+
+
+
+
+/*
+ *
+ * Tests an IP address in EAX for validity; return with 0 for bad, 1 for good.
+ * We used to refuse class E, but class E addresses are likely to become
+ * assignable unicast addresses in the near future.
+ *
+ */
+int ip_ok(uint32_t ip)
+{
+    if (ip == -1)          /* Refuse the all-one address */
+        goto bad;
+    if ((ip & 0xff) == 0)  /* Refuse network zero */
+        goto bad;
+    if ((ip & 0xff) == 0xff) /* Refuse loopback */
+        goto bad;
+    if ((ip & 0xf0) == 0xe0) /* Refuse class D */
+        goto bad;
+    
+    return 1;
+
+ bad:
+    return 0;
+}
+
+
+/*********************************************************************
+;
+; gendotquad
+;
+; Take an IP address (in network byte order) in EAX and
+; output a dotted quad string to ES:DI.
+; DI points to terminal null at end of string on exit.
+;
+ *********************************************************************/
+/**
+ * @param: dst, to store the converted ip string 
+ * @param: ip, the ip address that needed convert.
+ * 
+ * @return: the ip string length
+ */
+int gendotquad(char *dst, uint32_t ip)
+{
+    int part;
+    int i = 0, j;
+    char temp[4];
+    char *p = dst;
+    
+    for (; i < 4; i++) {
+        j = 0;
+        part = ip & 0xff;
+        do {
+            temp[j++] = (part % 10) + '0';
+        }while(part /= 10);
+        for (; j > 0; j--)
+            *p++ = temp[j-1];
+        *p++ = '.';
+        
+        ip >>= 8;
+    }
+    /* drop the last dot '.' and zero-terminate string*/
+    *(--p) = 0;
+    
+    return p - dst;
+}
+
+/*
+ * parse the ip_str and return the ip address with *res.
+ * return the the string address after the ip string
+ *
+ */
+char *parse_dotquad(char *ip_str, uint32_t *res)
+{
+    char *p = ip_str;
+    int  i  = 0;
+    uint8_t part = 0;
+    uint32_t ip  = 0;
+    
+    for (; i < 4; i++) {
+        while (is_digit(*p)) {
+            part = part * 10 + *p - '0';
+            p++;
+        }
+        if (i != 3 && *p != '.') 
+            return NULL;
+        
+        ip = (ip << 8) | part;
+        part = 0;
+        p++;
+    }
+    p --;
+    
+    *res = ip;
+    return p;    
+}
+/*
+ * the ASM pxenv function wrapper, return 1 if error, or 0
+ *
+ */    
+int pxe_call(int opcode, void *data)
+{
+    extern void pxenv(void);
+    com32sys_t in_regs, out_regs;
+    
+#if 0
+    printf("pxe_call op %04x data %p\n", opcode, data);
+#endif
+
+    memset(&in_regs, 0, sizeof in_regs);
+    
+    in_regs.ebx.w[0] = opcode;
+    in_regs.es       = SEG(data);
+    in_regs.edi.w[0] = OFFS(data);
+    call16(pxenv, &in_regs, &out_regs);
+    
+    return out_regs.eflags.l & EFLAGS_CF;  /* CF SET for fail */
+}
+
+/**
+ *
+ * Send ACK packet. This is a common operation and so is worth canning.
+ *
+ * @param: file,    TFTP block pointer
+ * @param: ack_num, Packet # to ack (network byte order)
+ * 
+ */
+void ack_packet(struct open_file_t *file, uint16_t ack_num)
+{
+    int err;
+    static __lowmem struct pxe_udp_write_pkt uw_pkt;
+    /* Packet number to ack */
+    ack_packet_buf[1]  = ack_num;
+    uw_pkt.lport      = file->tftp_localport;
+    uw_pkt.rport      = file->tftp_remoteport;
+    uw_pkt.sip        = file->tftp_remoteip;
+    uw_pkt.gip        = ((uw_pkt.sip ^ MyIP) & Netmask) ? Gateway : 0;
+    uw_pkt.buffer[0]  = OFFS_WRT(ack_packet_buf, 0);
+    uw_pkt.buffer[1]  = 0;    /* seems SEG and OFFS stuff doesn't work here */
+    uw_pkt.buffersize = 4;
+
+    err = pxe_call(PXENV_UDP_WRITE, &uw_pkt);
+#if 0
+    printf("sent %s\n", err ? "FAILED" : "OK");
+#endif
+}
+
+
+/**
+ * Get a DHCP packet from the PXE stack into the trackbuf
+ *
+ * @param:  type,  packet type
+ * @return: buffer size
+ * 
+ */
+int pxe_get_cached_info(int type)
+{
+    int err;
+    static __lowmem struct pxe_bootp_query_pkt bq_pkt;
+    printf(" %02x", type);
+    
+    bq_pkt.status     = 0;
+    bq_pkt.packettype = type;
+    bq_pkt.buffersize = 8192;
+    bq_pkt.buffer[0]  = OFFS_WRT(trackbuf, 0);
+    bq_pkt.buffer[1]  = 0;
+    
+    err = pxe_call(PXENV_GET_CACHED_INFO, &bq_pkt);
+    if (err) {
+        printf("%s %04x\n", err_pxefailed, err);
+        call16(kaboom, NULL, NULL);
+    }
+    
+    return bq_pkt.buffersize;
+}
+
+  
+
+#if GPXE
+
+/*
+ * Return 1 if and only if the buffer pointed to by
+ * url is a URL (contains ://)
+ *
+ */
+int is_url(char *url)
+{
+    
+    while (*url) {
+        if(! strncmp(url, "://", 3))
+            return 1;
+          
+        url++;
+    }    
+    return 0;
+}
+
+
+/*
+ * Return CF=0 if and only if the buffer pointed to by DS:SI is a URL 
+ * (contains ://) *and* the gPXE extensions API is available. No 
+ * registers modified.
+ */
+int is_gpxe(char *url)
+{
+    int err;
+    static __lowmem struct gpxe_file_api_check ac;
+    char *gpxe_warning_msg = "URL syntax, but gPXE extensions not detected, tring plain TFTP...\n";
+    
+    if (! is_url(url))
+        return 0;
+    
+    ac.size  = 20;
+    ac.magic = 0x91d447b2;
+    /* If has_gpxe is greater than one, means the gpxe status is unknow */
+    while (has_gpxe > 1)  {
+        err = pxe_call(PXENV_FILE_API_CHECK, &ac);
+        if (err || ac.magic != 0xe9c17b20) 
+            printf("%s\n", gpxe_warning_msg);
+        else             
+            has_gpxe = (~ac.provider & 0xffff) & 0x4b ? 0 : 1;
+        
+        if (!has_gpxe)
+            printf("%s\n", gpxe_warning_msg);
+    }
+
+    if (has_gpxe == 1) 
+        return 1;
+    else
+        return 0;   
+}
+
+/**
+ * Get a fresh packet from a gPXE socket
+ * @param: file -> socket structure
+ *
+ */
+void get_packet_gpxe(struct open_file_t *file)
+{
+    static __lowmem struct gpxe_file_read fr;
+    int err;
+    while (1) {
+        fr.filehandle = file->tftp_remoteport;
+        fr.buffer[0]  = file->tftp_pktbuf;
+        fr.buffer[1]  = PKTBUF_SEG;
+        fr.buffersize = PKTBUF_SIZE;
+        err = pxe_call(PXENV_FILE_READ, &fr);
+        if (!err)  /* successed */
+            break;
+
+        if (fr.status == PXENV_STATUS_TFTP_OPEN)
+            continue;
+        call16(kaboom, NULL, NULL);
+    }
+
+    file->tftp_bytesleft = fr.buffersize;
+    file->tftp_filepos  += fr.buffersize;
+    
+    if (file->tftp_bytesleft == 0)
+        file->tftp_filesize = file->tftp_filepos;
+    
+    /* if we're done here, close the file */
+    if (file->tftp_filesize > file->tftp_filepos) 
+        return;
+
+    /* Got EOF, close it */
+    file->tftp_goteof = 1;
+    pxe_call(PXENV_FILE_CLOSE, &fr);
+}
+#endif /* GPXE */
+
+
+/*
+ * mangle a filename pointed to by _src_ into a buffer pointed
+ * to by _dst_; ends on encountering any whitespace.
+ *
+ * The first four bytes of the manged name is the IP address of
+ * the download host, 0 for no host, or -1 for a gPXE URL.
+ *
+ */
+void pxe_mangle_name(char *dst, char *src)
+{
+    char *p = src;
+    uint32_t ip;
+    int i = 0;
+   
+#if GPXE
+    if (is_url(src))
+        goto prefix_done;
+#endif
+
+    ip = ServerIP;
+    if (*p == 0)
+        goto noip;
+    if (! strncmp(p, "::", 2))
+        goto gotprefix;
+    else {
+        while (*p && strncmp(p, "::", 2))
+            p ++;
+        if (! *p)
+            goto noip;
+        /*
+         * we have a :: prefix of ip sort, it could be either a DNS
+         * name or dot-quad IP address. Try the dot-quad first.
+         */
+        p = src;
+        if ((p = parse_dotquad(p, &ip)) && !strncmp(p, "::", 2))
+            goto gotprefix;
+        else {
+#if 0
+            extern void dns_resolv(void);
+            call16(dns_resolv, regs, regs);
+            p = (char *)MK_PTR(regs->ds, regs->esi.w[0]);
+            ip = regs->eax.l;
+            if (! strncmp(p, "::", 2))
+                if (ip)
+                   goto gotprefix;
+#endif
+        }
+    }
+ noip:
+    p = src;
+    ip = 0;
+    goto prefix_done;
+
+ gotprefix:
+    p += 2;      /* skip double colon */
+  
+ prefix_done:
+    *(uint32_t *)dst = ip;  
+    dst += 4;
+    i = FILENAME_MAX - 5;
+
+    do {
+        if (*p <= ' ')
+            break;
+        *dst++ = *p++;
+    }while (i--);
+
+    i ++;
+    while (i) {
+        *dst++ = 0;
+        i --;
+    }    
+    
+#if 0
+    printf("the name before mangling: ");
+    dump16(src);
+    printf("the name after mangling:  ");
+    dump16((char *)MK_PTR(regs->ds, regs->edi.w[0]));
+#endif
+}
+
+        
+/*
+ *
+ ;
+ ; Get a fresh packet if the buffer is drained, and we haven't hit
+ ; EOF yet.  The buffer should be filled immediately after draining!
+ ;
+ ; expects fs -> pktbuf_seg and ds:si -> socket structure
+ ;
+*/
+void fill_buffer(struct open_file_t *file)
+{
+    int err;
+    int last_pkt;
+    const uint8_t *timeout_ptr = TimeoutTable;
+    uint8_t timeout;
+    uint16_t buffersize;
+    uint16_t old_time;
+    void *data = NULL;
+    static __lowmem struct pxe_udp_read_pkt pkt;
+        
+    if (file->tftp_bytesleft || file->tftp_goteof)
+        return;
+
+#if GPXE
+    if (file->tftp_localport == 0xffff) {
+        get_packet_gpxe(file);
+        return;
+    }
+#endif
+
+
+    /*
+     * Start by ACKing the previous packet; this should cause
+     * the next packet to be sent.
+     */
+ ack_again:   
+    ack_packet(file, file->tftp_lastpkt);
+    
+    timeout_ptr = TimeoutTable;    
+    timeout = *timeout_ptr++;
+    old_time = BIOS_timer;
+    while (timeout) {
+        pkt.buffer[0]  = file->tftp_pktbuf;
+        pkt.buffer[1]  = PKTBUF_SEG;
+        pkt.buffersize = PKTBUF_SIZE;
+        pkt.sip        = file->tftp_remoteip;
+        pkt.dip        = MyIP;
+        pkt.rport      = file->tftp_remoteport;
+        pkt.lport      = file->tftp_localport;
+        err = pxe_call(PXENV_UDP_READ, &pkt);
+        if (err) {
+            if (BIOS_timer == old_time) {
+               printf(".");
+                continue;
+           }
+           printf("+");
+            
+            timeout--;         /* decrease one timer tick */
+            if (!timeout) {
+                timeout = *timeout_ptr++;
+               if (!timeout)
+                   break;
+           }
+            continue;
+        }
+    
+        if (pkt.buffersize < 4)  /* Bad size for a DATA packet */
+            continue;    
+
+        data = MK_PTR(PKTBUF_SEG, file->tftp_pktbuf);
+        if (*(uint16_t *)data != TFTP_DATA)    /* Not a data packet */
+            continue;
+                
+        /* If goes here, recevie OK, break */
+        break;
+    }
+
+    /* time runs out */
+    if (timeout == 0)
+        call16(kaboom, NULL, NULL);
+    
+    last_pkt = file->tftp_lastpkt;
+    last_pkt = ntohs(last_pkt);       /* Host byte order */
+    last_pkt++;
+    last_pkt = htons(last_pkt);       /* Network byte order */
+    if (*(uint16_t *)(data + 2) != last_pkt) {
+        /*
+         * Wrong packet, ACK the packet and try again.
+         * This is presumably because the ACK got lost,
+         * so the server just resent the previous packet.
+         */
+#if 0
+       printf("Wrong packet, wanted %04x, got %04x\n", htons(last_pkt), htons(*(uint16_t *)(data+2)));
+#endif
+        goto ack_again;
+    }
+    
+    /* It's the packet we want.  We're also EOF if the size < blocksize */
+    file->tftp_lastpkt = last_pkt;    /* Update last packet number */
+    buffersize = pkt.buffersize - 4; /* Skip TFTP header */
+    file->tftp_dataptr = file->tftp_pktbuf + 4;
+    file->tftp_filepos += buffersize;
+    file->tftp_bytesleft = buffersize;
+    if (buffersize < file->tftp_blksize) {
+        /* it's the last block, ACK packet immediately */
+        ack_packet(file, *(uint16_t *)(data + 2));
+
+        /* Make sure we know we are at end of file */
+        file->tftp_filesize = file->tftp_filepos;
+        file->tftp_goteof   = 1;
+    }
+}
+
+
+/**
+ * getfssec: Get multiple clusters from a file, given the starting cluster.
+ * In this case, get multiple blocks from a specific TCP connection.
+ *
+ * @param: fs, the fs_info structure address, in pxe, we don't use this.
+ * @param: buf, buffer to store the read data
+ * @param: openfile, TFTP socket pointer
+ * @param: blocks, 512-byte block count; 0FFFFh = until end of file
+ * 
+ * @return: the bytes read
+ *
+ */
+uint32_t pxe_getfssec(struct fs_info *fs, char *buf, 
+                      void *open_file, int blocks, int *have_more)
+{
+    struct open_file_t *file = (struct open_file_t *)open_file;
+    int count = blocks;
+    int chunk;
+    int bytes_read = 0;
+
+    sti();    
+    fs = NULL;      /* drop the compile warning message */
+
+    count <<= TFTP_BLOCKSIZE_LG2;
+    while (count) {
+        fill_buffer(file);         /* If we have no 'fresh' buffer, get it */              
+        if (! file->tftp_bytesleft)
+            break;
+        
+        chunk = count;
+        if (chunk > file->tftp_bytesleft)
+            chunk = file->tftp_bytesleft;
+        file->tftp_bytesleft -= chunk;
+        memcpy(buf, MK_PTR(PKTBUF_SEG, file->tftp_dataptr), chunk);
+       file->tftp_dataptr += chunk;
+        buf += chunk;
+        bytes_read += chunk;
+        count -= chunk;
+    }
+
+    
+    if (file->tftp_bytesleft || (file->tftp_filepos < file->tftp_filesize)) {
+       fill_buffer(file);
+        *have_more = 1;
+    } else if (file->tftp_goteof) {
+        /* 
+         * The socket is closed and the buffer dranined
+         * close socket structure and re-init for next user
+         */
+        free_socket(file);
+        *have_more = 0;
+    }
+    
+    return bytes_read;
+ }
+
+
+/*
+ * Fill the packet tail with the tftp informations then retures the lenght
+ */
+int fill_tail(char *dst)
+{
+    char *p = dst;
+    strcpy(p, mode);
+    p += strlen(mode) + 1;
+    
+    strcpy(p, tsize_str);
+    p += strlen(tsize_str) + 1;
+    
+    strcpy(p, "0");
+    p += 2;
+    
+    strcpy(p, blksize_str);
+    p += strlen(blksize_str) + 1;   
+
+    strcpy(p, asciidec);
+    p += strlen(asciidec) + 1;
+
+    return p - dst;
+}
+
+
+struct tftp_options {
+    char *str_ptr;        /* string pointer */
+    int   str_len;        /* string lenght */
+    int   offset;         /* offset into socket structre */
+};
+struct tftp_options tftp_options[2];
+
+inline void init_options()
+{
+    tftp_options[0].str_ptr = tsize_str;
+    tftp_options[0].str_len = tsize_len;
+    tftp_options[0].offset  = (int)&((struct open_file_t *)0)->tftp_filesize;
+    
+    tftp_options[1].str_ptr = blksize_str;
+    tftp_options[1].str_len = blksize_len;
+    tftp_options[1].offset  = (int)&((struct open_file_t *)0)->tftp_blksize;
+}
+
+/*
+ *
+ *
+ * searchdir:
+ *
+ *     Open a TFTP connection to the server
+ *
+ *          On entry:
+ *             DS:DI   = mangled filename
+ *          If successful:
+ *             ZF clear
+ *             SI      = socket pointer
+ *             EAX     = file length in bytes, or -1 if unknown
+ *          If unsuccessful
+ *             ZF set
+ *
+ */
+void pxe_searchdir(char *filename, struct file *file)
+{
+    char *buf = packet_buf;
+    char *p = filename;
+    char *options;
+    char *src, *dst;
+    char *data;
+    struct open_file_t *open_file;
+    static __lowmem struct pxe_udp_write_pkt uw_pkt;
+    static __lowmem struct pxe_udp_read_pkt  ur_pkt;
+    static __lowmem struct gpxe_file_open fo;
+    static __lowmem struct gpxe_get_file_size gs;
+    struct tftp_options *tftp_opt = tftp_options;
+    int i = 0;
+    int tftp_opts = sizeof tftp_options / sizeof tftp_options[0];
+    int err;
+    int buffersize;
+    const uint8_t  *timeout_ptr;
+    uint8_t  timeout;
+    uint16_t oldtime;
+    uint16_t tid;
+    uint16_t opcode;
+    uint16_t blk_num;
+    uint32_t ip;
+    uint32_t filesize = 0;
+    uint32_t *data_ptr;
+    
+    init_options();
+    open_file = allocate_socket();
+    if (!open_file) 
+        goto done;
+    
+    timeout_ptr = TimeoutTable;   /* Reset timeout */
+
+ sendreq:
+    uw_pkt.buffer[0] = OFFS_WRT(buf, 0);
+    uw_pkt.buffer[1] = 0;
+    *(uint16_t *)buf = TFTP_RRQ;  /* TFTP opcode */
+    buf += 2;
+    
+    ip = *(uint32_t *)p;      /* ip <- server override (if any) */
+    p += 4;
+    if (ip == 0) {
+        /* Have prefix */
+        strcpy(buf, PathPrefix);
+        buf += strlen(PathPrefix);
+        ip = ServerIP;            /* Get the default server */
+    }
+
+    strcpy(buf, p);                /* Copy the filename */
+    buf += strlen(p) + 1;          /* advance the pointer, null char included */
+    
+#if GPXE
+    if (is_gpxe(packet_buf + 2)) {
+        fo.status = PXENV_STATUS_BAD_FUNC;
+        fo.filename[0] = OFFS_WRT(packet_buf + 2, 0);
+        fo.filename[1] = 0;
+        err = pxe_call(PXENV_FILE_OPEN, &fo);
+        if (err) 
+            goto done;
+        
+        open_file->tftp_localport = -1;
+        open_file->tftp_remoteport = fo.filehandle;
+        gs.filehandle = fo.filehandle;
+        
+#if 0
+        err = pxe_call(PXENV_GET_FILE_SIZE, &gs);
+        if (!err)
+            filesize = gs.filesize;
+        else
+#endif
+            filesize = -1;
+        open_file->tftp_filesize = -1;
+        goto done;
+    }
+#endif /* GPXE */
+    
+    open_file->tftp_remoteip = ip;
+    tid = open_file->tftp_localport;   /* TID(local port No) */
+    uw_pkt.sip = ip;
+    uw_pkt.gip = ((uw_pkt.sip ^ MyIP) & Netmask) ? Gateway : 0;
+    uw_pkt.lport = tid;
+    uw_pkt.rport = ServerPort;
+    buf += fill_tail(buf);
+    uw_pkt.buffersize = buf - packet_buf;
+    err = pxe_call(PXENV_UDP_WRITE, &uw_pkt);
+    if (err || uw_pkt.status != 0)
+        goto failure;            /* In fact, the 'failure' target will not do a failure thing; 
+                                    it will move on to the next timeout, then tries again until
+                                    _real_ time out */
+    
+    /*
+     * Danger, Will Robinson! We need to support tiemout
+     * and retry lest we just lost a packet ...
+     */
+    
+    /* Packet transmitted OK, now we need to receive */
+    timeout = *timeout_ptr++;
+    oldtime = BIOS_timer;
+    while (timeout) {
+        buf = packet_buf;
+        ur_pkt.buffer[0] = OFFS_WRT(buf, 0);
+        ur_pkt.buffer[1] = 0;
+        ur_pkt.buffersize = 2048;
+        ur_pkt.dip = MyIP;
+        ur_pkt.lport = tid;
+        err = pxe_call(PXENV_UDP_READ, &ur_pkt);
+        if (err) {
+            if (oldtime == BIOS_timer)
+                continue;
+            timeout --;  /* Decrease one timer tick */
+            if (!timeout) 
+                goto failure;
+        }
+
+        /* Make sure the packet actually came from the server */
+        if (ur_pkt.sip == open_file->tftp_remoteip)
+            break;
+    }
+          
+    /* Got packet; reset timeout */
+    timeout_ptr = TimeoutTable;
+    open_file->tftp_remoteport = ur_pkt.rport;
+    
+    /* filesize <- -1 == unknow */
+    open_file->tftp_filesize = -1;
+    /* Default blksize unless blksize option negotiated */
+    open_file->tftp_blksize = TFTP_BLOCKSIZE;
+    buffersize = ur_pkt.buffersize - 2;  /* bytes after opcode */
+    if (buffersize < 0)
+        goto failure;                     /* Garbled reply */
+    
+    /*
+     * Get the opcode type, and parse it 
+     */
+    opcode = *(uint16_t *)packet_buf;
+    if (opcode == TFTP_ERROR) {
+        filesize = 0;
+        open_file = NULL;
+        goto done;                     /* ERROR reply; don't try again */
+    } else if (opcode == TFTP_DATA) {
+        /*
+         * If the server doesn't support any options, we'll get a
+         * DATA reply instead of OACK. Stash the data in the file
+         * buffer and go with the default value for all options...
+         *
+         * We got a DATA packet, meaning no options are
+         * suported. Save the data away and consider the 
+         * length undefined, *unless* this is the only 
+         * data packet... 
+         */
+        buffersize -= 2;
+        if (buffersize < 0)
+            goto failure;
+        data = packet_buf + 2;
+        blk_num = *(uint16_t *)data;
+        data += 2;
+        if (blk_num != htons(1))
+            goto failure;
+        open_file->tftp_lastpkt = blk_num;
+        if (buffersize > TFTP_BLOCKSIZE)
+            goto err_reply;  /* Corrupt */
+        else if (buffersize < TFTP_BLOCKSIZE) {
+            /* 
+             * This is the final EOF packet, already...
+             * We know the filesize, but we also want to 
+             * ack the packet and set the EOF flag.
+             */
+            open_file->tftp_filesize = buffersize;
+            open_file->tftp_goteof = 1;
+            ack_packet(open_file, blk_num);
+        }
+
+        open_file->tftp_bytesleft = buffersize;
+        open_file->tftp_dataptr = open_file->tftp_pktbuf;
+        memcpy(MK_PTR(PKTBUF_SEG, open_file->tftp_pktbuf), data, buffersize);        
+        goto done;   
+        
+    } else if (opcode == TFTP_OACK) {
+        /* 
+         * Now we need to parse the OACK packet to get the transfer
+         * and packet sizes.
+         */
+        if (!buffersize)
+            goto done;       /* No options acked */
+        
+        /*
+         * If we find an option which starts with a NUL byte,
+         * (a null option), we're either seeing garbage that some
+         * TFTP servers add to the end of the packet, or we have
+         * no clue how to parse the rest of the packet (what is
+         * an option name and what is a value?)  In either case,
+         * discard the rest.
+         */
+        options = packet_buf + 2;
+        if (*options == 0)
+            goto done;
+
+        dst = src = options;
+        while (buffersize--) {
+            if (*src == 0)
+                break;          /* found a final null */
+            *dst++ = *src++ | 0x20;
+            if (!buffersize)
+                goto done;  /* found no final null */
+        }
+        
+        /* 
+         * Parse option pointed to by options; guaranteed to be null-terminated
+         */
+        p = options;
+        do {
+            tftp_opt = tftp_options;
+            for (i = 0; i < tftp_opts; i++) {
+                if (!strncmp(p, tftp_opt->str_ptr,tftp_opt->str_len))
+                    break;
+                tftp_opt++;
+            }
+            if (i == tftp_opts)
+                goto err_reply; /* Non-negotitated option returned, no idea what it means ...*/
+            
+            p += tftp_opt->str_len;
+            
+            /* get the address of the filed that we want to write on */
+            data_ptr = (uint32_t *)((char *)file + tftp_opt->offset);
+            *data_ptr = 0;
+            
+            /* do convert a number-string to decimal number, just like atoi */
+            while (buffersize--) {
+                if (*p == 0)
+                    break;              /* found a final null */
+                if (*p > '9')
+                    goto err_reply;     /* Not a decimal digit */
+                *data_ptr = *data_ptr * 10 + *p++ - '0';
+            }
+            
+        }while (buffersize);
+        
+    } else {
+        extern char tftp_proto_err[];
+    err_reply:
+    
+        uw_pkt.rport = open_file->tftp_remoteport;
+        uw_pkt.buffer[0] = OFFS_WRT(tftp_proto_err, 0);
+        uw_pkt.buffer[1] = 0;
+        uw_pkt.buffersize = 24;
+        pxe_call(PXENV_UDP_WRITE, &uw_pkt);
+        printf("TFTP server sent an incomprehesible reply\n");
+        call16(kaboom, NULL, NULL);
+    }
+    
+    filesize = open_file->tftp_filesize;
+        
+ done:
+    if (!filesize) 
+        free_socket(open_file);
+    file->file_len  = filesize;
+    file->open_file = (void *)open_file;
+    return;
+
+ failure:
+    timeout_ptr++;
+    if (*timeout_ptr)
+        goto sendreq;  /* Try again */
+}
+
+/*
+ * Store standard filename prefix
+ */
+void get_prefix(void)
+{
+    int len;
+    char *p;
+    char c;
+    
+    if (DHCPMagic & 0x04)         /* Did we get a path prefix option */
+        goto got_prefix;
+    
+    strcpy(PathPrefix, BootFile);
+    len = strlen(PathPrefix);
+    p = &PathPrefix[len - 1]; 
+
+    while (len--) {
+        c = *p--;
+        c |= 0x20;
+        
+        c = (c >= '0' && c <= '9') || 
+            (c >= 'a' && c <= 'z') || 
+            (c == '.' || c == '-');
+        if (!c)
+            break;
+    };
+    
+    if (len < 0)
+        p --;
+
+    *(p + 2) = 0;                /* Zero-terminate after delimiter */
+    
+ got_prefix:
+    printf("%s%s\n", tftpprefix_msg, PathPrefix);
+    strcpy(CurrentDirName, PathPrefix);
+}
+
+ /**
+  * try to load a config file, if found, return 1, or return 0
+  *
+  */   
+int try_load(com32sys_t *regs)
+{
+    extern char KernelName[];
+    char *config_name = (char *)MK_PTR(regs->ds, regs->edi.w[0]);
+
+    get_prefix();
+    
+    printf("Trying to load: %-50s ", config_name);
+    pxe_mangle_name(KernelName, config_name);
+    
+    regs->edi.w[0] = OFFS_WRT(KernelName, 0);
+    call16(core_open, regs, regs);
+    if (regs->eflags.l & EFLAGS_ZF) {
+        printf("   [FAILED]\n");
+        return 0;
+    } else {
+        printf("   [  OK  ]\n");
+        return 1;
+    }
+}
+
+
+ /*
+  * load configuration file
+  *
+  */
+void pxe_load_config(com32sys_t *regs)
+{
+    extern void no_config(void);
+    char *cfgprefix = "pxelinux.cfg/";
+    char *default_str = "default";
+    char *config_file;         /* Pointer to the variable suffix */
+    char *p;
+   
+    uint8_t *uuid_ptr;
+
+    int tries = 8;
+    char *last;
+    
+    if (DHCPMagic & 0x02) {
+        /* We got a DHCP option, try it first */
+        if (try_load(regs))
+            return;
+    }
+    
+    memcpy(ConfigName, cfgprefix, strlen(cfgprefix));
+    config_file = ConfigName + strlen(cfgprefix);
+    /*
+     * Have to guess config file name ...
+     */
+    
+    /* Try loading by UUID */
+    if (HaveUUID) {
+        uuid_ptr  = uuid_dashes;
+       p = config_file;
+        while (*uuid_ptr) {
+            int len = *uuid_ptr;
+            char *src = UUID;
+            
+            lchexbytes(p, src, len);
+            p += len * 2;
+            src += len;
+            uuid_ptr++;
+            *p++ = '-';
+        }
+        /* Remove last dash and zero-terminate */
+       *--p = '\0';
+        regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+        if (try_load(regs))
+            return;
+    }
+
+    /* Try loading by MAC address */
+    strcpy(config_file, MACStr);
+    regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+    if (try_load(regs))
+        return;
+    
+#if 0
+    printf("MY IP: %p(%X)\n", MyIP, *(uint32_t *)MyIP);
+ #endif
+    
+    /* Nope, try hexadecimal IP prefixes... */
+    uchexbytes(config_file, (uint8_t *)&MyIP, 4);     /* Convet to hex string */
+    last = &config_file[8];
+    while (tries) {
+        *last = '\0';        /* Zero-terminate string */
+        regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+        if (try_load(regs))
+            return;
+        last--;           /* Drop one character */
+        tries--;
+    };
+    
+    /* Final attempt: "default" string */
+    strcpy(config_file, default_str);
+    regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+    if (try_load(regs))
+        return;
+
+    /* call16(no_config, NULL, NULL); */
+}
+   
+  
+
+/*
+ * Generate the botif string, and the hardware-based config string 
+ */
+void make_bootif_string(void)
+{
+    char *bootif_str = "BOOTIF=";
+    uint8_t *src = &MACType;      /* MACType just followed by MAC */
+    char *dst;
+    int i = MACLen + 1;         /* MACType included */
+    
+    strcpy(BOOTIFStr, bootif_str);
+    dst = strchr(BOOTIFStr, '\0');
+    for (; i > 0; i--) {
+        lchexbytes(dst, src, 1);
+        dst += 2;
+        src += 1;
+        *dst++ = '-';
+    }
+    *(dst - 1) = 0;   /* Drop the last '-' and null-terminate string */
+
+#if 0
+    printf("%s\n", BOOTIFStr);
+#endif
+}
+/*
+  ;
+  ; genipopt
+  ;
+  ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
+  ; option into IPOption based on a DHCP packet in trackbuf.
+  ; Assumes CS == DS == ES.
+  ;
+*/
+void genipopt(void)
+{
+    char *p = IPOption;
+    int ip_len;
+    
+    strcpy(p, "ip=");
+    p += 3;
+    
+    ip_len = gendotquad(p, MyIP);
+    p += ip_len;
+    *p++ = ':';
+
+    ip_len = gendotquad(p, ServerIP);
+    p += ip_len;
+    *p++ = ':';
+
+    ip_len = gendotquad(p, Gateway);
+    p += ip_len;
+    *p++ = ':';
+    
+    ip_len = gendotquad(p, Netmask);
+}
+    
+
+/* Generate ip= option and print the ip adress */
+void ip_init(void)
+{
+    int ip = MyIP;
+    char *myipaddr_msg = "My IP address seems to be ";
+    
+    genipopt();
+
+    gendotquad(DotQuadBuf, ip);
+    
+    ip = ntohl(ip);
+    printf("%s %08X %s\n", myipaddr_msg, ip, DotQuadBuf);
+    
+    printf("%s\n", IPOption);
+}
+
+/*
+ * Validity check on possible !PXE structure in buf
+ * return 1 for success, 0 for failure.
+ *
+ */
+int is_pxe(char *buf)
+{
+    int i = buf[4];
+    uint8_t sum = 0;
+
+    if (memcmp(buf, "!PXE", 4) || i < 0x58)
+        return 0;
+
+    while (i--)
+        sum += *buf++;
+
+    if (sum == 0)
+        return 1;
+    else
+        return 0;
+}
+
+/**
+ * Just like is_pxe, it checks PXENV+ structure
+ *
+ */
+int is_pxenv(char *buf)
+{
+    int i = buf[8];
+    uint8_t sum = 0;
+    
+    if (memcmp(buf, "PXENV+", 6) || i < 0x28)
+        return 0;
+
+    while (i--)
+        sum += *buf++;
+
+    if (sum == 0)
+        return 1;
+    else
+        return 0;
+}
+        
+
+
+/*********************************************************************
+;
+; memory_scan_for_pxe_struct:
+; memory_scan_for_pxenv_struct:
+;
+;      If none of the standard methods find the !PXE/PXENV+ structure,
+;      look for it by scanning memory.
+;
+;      On exit, if found:
+;              ZF = 1, ES:BX -> !PXE structure
+;      Otherwise:
+;              ZF = 0
+;
+;      Assumes DS == CS
+;      Clobbers AX, BX, CX, DX, SI, ES
+;
+ ********************************************************************/
+
+inline int memory_scan(uint16_t seg, int (*func)(char *))
+{
+    while (seg < 0xA000) {
+        if (func(MK_PTR(seg, 0)))
+            return 1;       /* found it */
+        seg++;
+    }
+    return 0;
+}
+    
+int memory_scan_for_pxe_struct()
+{
+    extern uint16_t BIOS_fbm;  /* Starting segment */
+    uint16_t seg = BIOS_fbm << (10 - 4);
+
+    return memory_scan(seg, is_pxe);
+}
+    
+int memory_scan_for_pxenv_struct()
+{
+    uint16_t seg = 0x1000;
+    
+    return memory_scan(seg, is_pxenv);
+}
+
+/*
+ * Find the !PXE structure; we search for the following, in order:
+ *
+ * a. !PXE structure as SS:[SP + 4]
+ * b. PXENV+ structure at [ES:BX]
+ * c. INT 1Ah AX=0x5650 -> PXENV+
+ * d. Search memory for !PXE
+ * e. Search memory for PXENV+
+ *
+ * If we find a PXENV+ structure, we try to find a !PXE structure from 
+ * if if the API version is 2.1 or later 
+ *
+ */
+void pxe_init()
+{
+    char plan = 'A';
+    uint16_t seg, off;
+    uint16_t code_seg, code_len;
+    uint16_t data_seg, data_len;
+    char *base = GET_PTR(InitStack);
+
+    /* Assume API version 2.1 */
+    APIVer = 0x201;
+    
+    /* Plan A: !PXE structure as SS:[SP + 4] */
+    off = *(uint16_t *)(base + 48);
+    seg = *(uint16_t *)(base + 50);
+    if (is_pxe(MK_PTR(seg, off))) 
+        goto have_pxe;
+
+    /* Plan B: PXENV+ structure at [ES:BX] */
+    plan++;
+    off = *(uint16_t *)(base + 24);  /* Original BX */
+    seg = *(uint16_t *)(base + 4);   /* Original ES */
+    if (is_pxenv(MK_PTR(seg, off)))
+        goto have_pxenv;
+
+    /* 
+     * Plan C: PXENV+ structure via INT 1Ah AX=5650h 
+     * 
+     * for now, we just skip it since it must be run in Real mode 
+     */
+    extern void plan_c(void);
+    plan++;
+#if 0
+    goto plan_D;               
+    call16(plan_c, regs, regs);
+    if (((regs->eflags.l & EFLAGS_CF) == 0) && (regs->eax.w[0] == 0x564e)) {
+        seg = regs->es;
+        off = regs->ebx.w[0];
+        if (is_pxenv(MK_PTR(seg, off)))
+            goto have_pxenv;
+    }
+
+ plan_D:
+#endif
+    /* Plan D: !PXE memory scan */
+    plan++;
+    if (memory_scan_for_pxe_struct())
+        goto have_pxe;
+    
+    /* Plan E: PXENV+ memory scan */
+    plan++;
+    if (memory_scan_for_pxenv_struct())
+        goto have_pxenv;
+
+    /* Found nothing at all !! */
+    printf("%s\n", err_nopxe);
+    call16(kaboom, NULL, NULL);
+    
+ have_pxenv:
+    StructPtr[0] = off;
+    StructPtr[1] = seg;
+    base = MK_PTR(seg, off);
+    APIVer = *(uint16_t *)(base + 6);
+    printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
+
+    /* if the API version number is 0x0201 or higher, use the !PXE structure */
+    if (APIVer >= 0x201) {
+        if (*(char *)(base + 8) < 0x2c) {          /* Length of PXENV+ structure in bytes */
+            if (memory_scan_for_pxe_struct())
+                goto have_pxe;
+        }
+    
+        off = *(uint16_t *)(base + 0x28);
+        seg = *(uint16_t *)(base + 0x2a);
+        if (is_pxe(MK_PTR(seg, off)))
+            goto have_pxe;    
+        /*
+         * Nope, !PXE structuremissing despite API 2.1+, or at least
+         * the pointer is missing. Do a last-ditch attempt to find it
+         */
+        if (memory_scan_for_pxe_struct())
+            goto have_pxe;
+    }
+    
+    /* Otherwise, no dice, use PXENV+ structure */
+    data_len = *(uint16_t *)(base + 0x22);  /* UNDI data len */
+    data_seg = *(uint16_t *)(base + 0x20);  /* UNDI data seg */
+    code_len = *(uint16_t *)(base + 0x26);  /* UNDI code len */
+    code_seg = *(uint16_t *)(base + 0x24);  /* UNDI code seg */
+    PXEEntry = *(far_ptr_t *)(base + 0x0a);  /* PXENV+ entry point */
+    printf("PXENV+ entry point found (we hope) at ");
+    goto have_entrypoint;
+
+ have_pxe:
+    StructPtr[0] = off;
+    StructPtr[1] = seg;
+    base = MK_PTR(seg, off);
+    
+    data_len = *(uint16_t *)(base + 0x2e);  /* UNDI data len */
+    data_seg = *(uint16_t *)(base + 0x28);  /* UNDI data seg */
+    code_len = *(uint16_t *)(base + 0x36);  /* UNDI code len */
+    code_seg = *(uint16_t *)(base + 0x30);  /* UNDI code seg */
+    PXEEntry = *(far_ptr_t *)(base + 0x10);  /* !PXE entry point */
+    printf("!PXE entry point found (we hope) at ");
+
+ have_entrypoint:
+    printf("%04X:%04X via plan %c\n", PXEEntry.seg, PXEEntry.offs, plan);
+    printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
+    code_seg = code_seg + ((code_len + 15) >> 4);
+    
+    printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
+    data_seg = data_seg + ((data_len + 15) >> 4);
+    
+    
+    RealBaseMem = MAX(code_seg,data_seg) >> 6; /* Convert to kilobytes */
+    //regs->es = regs->ds;                       /* Restore the es segment */
+}                                  
+
+/*
+ * Initialize UDP stack 
+ *
+ */
+void udp_init(void)
+{
+    int err;
+    static __lowmem struct pxe_udp_open_pkt uo_pkt;
+    uo_pkt.sip = MyIP;
+    err = pxe_call(PXENV_UDP_OPEN, &uo_pkt);
+    if (err || uo_pkt.status) {
+        printf("%s", err_udpinit);
+        printf("%d\n", uo_pkt.status);
+        call16(kaboom, NULL, NULL);
+    }
+}
+  
+    
+/*
+ * Network-specific initialization
+ */   
+void network_init()
+{   
+    struct bootp_t *bp = (struct bootp_t *)trackbuf;
+    int pkt_len;
+
+    *LocalDomain = 0;   /* No LocalDomain received */
+    
+    /*
+     * Get the DHCP client identifiers (query info 1)
+     */
+    printf("%s", get_packet_msg);
+    pkt_len = pxe_get_cached_info(1);
+    parse_dhcp(pkt_len);
+    /*
+      ; We don't use flags from the request packet, so
+      ; this is a good time to initialize DHCPMagic...
+      ; Initialize it to 1 meaning we will accept options found;
+      ; in earlier versions of PXELINUX bit 0 was used to indicate
+      ; we have found option 208 with the appropriate magic number;
+      ; we no longer require that, but MAY want to re-introduce
+      ; it in the future for vendor encapsulated options.
+    */
+    *(char *)&DHCPMagic = 1;
+
+    
+    /*
+     * Get the BOOTP/DHCP packet that brought us file (and an IP
+     * address). This lives in the DHCPACK packet (query info 2)
+     */
+    pkt_len = pxe_get_cached_info(2);
+    parse_dhcp(pkt_len);
+    /*
+     * Save away MAC address (assume this is in query info 2. If this 
+     * turns out to be problematic it might be better getting it from
+     * the query info 1 packet
+     */
+    MACLen = bp->hardlen > 16 ? 0 : bp->hardlen;
+    MACType = bp->hardware;
+    memcpy(MAC, bp->macaddr, MACLen);
+    
+
+    /*
+     * Get the boot file and other info. This lives in the CACHED_REPLY
+     * packet (query info 3)
+     */
+    pkt_len = pxe_get_cached_info(3);
+    parse_dhcp(pkt_len);
+    printf("\n");
+
+
+
+    make_bootif_string();
+    ip_init();
+
+    /*
+     * Check to see if we got any PXELINUX-specific DHCP options; in particular,
+     * if we didn't get the magic enable, do not recognize any other options.
+     */
+    if ((DHCPMagic & 1) == 0)
+        DHCPMagic = 0;
+
+    udp_init();
+}
+
+/*
+ * Initialize pxe fs
+ *
+ */
+int pxe_fs_init(struct fs_info *fs)
+{
+    fs = NULL;    /* drop the compile warning message */
+    
+    /* do the pxe initialize */
+    pxe_init();
+
+    /* Network-specific initialization */
+    network_init();
+
+    return 0;
+}
+    
+const struct fs_ops pxe_fs_ops = {
+    .fs_name       = "pxe",
+    .fs_init       = pxe_fs_init,
+    .searchdir     = pxe_searchdir,
+    .getfssec      = pxe_getfssec,
+    .mangle_name   = pxe_mangle_name,
+    .unmangle_name = NULL,
+    .load_config   = pxe_load_config
+};
index b866369..6c00958 100644 (file)
@@ -161,23 +161,31 @@ tftp_pktbuf       resw 1                  ; Packet buffer offset
 ; Memory below this point is reserved for the BIOS and the MBR
 ;
                section .earlybss
+                global trackbuf
 trackbufsize   equ 8192
 trackbuf       resb trackbufsize       ; Track buffer goes here
                ; ends at 2800h
 
+               ; These fields save information from before the time
+               ; .bss is zeroed... must be in .earlybss
+               global InitStack
+InitStack      resd 1
+
                section .bss16
+                global Files
                alignb open_file_t_size
 Files          resb MAX_OPEN*open_file_t_size
 
                alignb FILENAME_MAX
+                global BootFile, PathPrefix, DotQuadBuf, IPOption
 BootFile       resb 256                ; Boot file from DHCP packet
 PathPrefix     resb 256                ; Path prefix derived from boot file
 DotQuadBuf     resb 16                 ; Buffer for dotted-quad IP address
 IPOption       resb 80                 ; ip= option buffer
-InitStack      resd 1                  ; Pointer to reset stack (SS:SP)
 PXEStack       resd 1                  ; Saved stack during PXE call
 
                alignb 4
+                global DHCPMagic, OverLoad, RebootTime, APIVer, RealBaseMem
 RebootTime     resd 1                  ; Reboot timeout, if set by option
 StrucPtr       resd 1                  ; Pointer to PXENV+ or !PXE structure
 APIVer         resw 1                  ; PXE API version found
@@ -187,6 +195,7 @@ OverLoad    resb 1                  ; Set if DHCP packet uses "overloading"
 DHCPMagic      resb 1                  ; PXELINUX magic flags
 
 ; The relative position of these fields matter!
+                global MACStr, MACLen, MACType, MAC, BOOTIFStr
 MAC_MAX                equ  32                 ; Handle hardware addresses this long
 MACLen         resb 1                  ; MAC address len
 MACType                resb 1                  ; MAC address type
@@ -195,6 +204,7 @@ BOOTIFStr   resb 7                  ; Space for "BOOTIF="
 MACStr         resb 3*(MAC_MAX+1)      ; MAC address as a string
 
 ; The relative position of these fields matter!
+                global UUID
 UUIDType       resb 1                  ; Type byte from DHCP option
 UUID           resb 16                 ; UUID, from the PXE stack
 UUIDNull       resb 1                  ; dhcp_copyoption zero-terminates
@@ -212,6 +222,7 @@ pxe_unload_stack_pkt_len    equ $-pxe_unload_stack_pkt
                ; BOOTP/DHCP packet buffer
 
                section .bss16
+                global packet_buf
                alignb 16
 packet_buf     resb 2048               ; Transfer packet
 packet_buf_size        equ $-packet_buf
@@ -277,42 +288,20 @@ _start1:
                call writestr_early
 
 ;
-; Assume API version 2.1, in case we find the !PXE structure without
-; finding the PXENV+ structure.  This should really look at the Base
-; Code ROM ID structure in have_pxe, but this is adequate for now --
-; if we have !PXE, we have to be 2.1 or higher, and we don't care
-; about higher versions than that.
+; do fs initialize
 ;
-               mov word [APIVer],0201h
+                extern pxe_fs_ops
+               mov eax,pxe_fs_ops
+                pm_call fs_init
+                jmp next
 
 ;
-; Now we need to find the !PXE structure.
-; We search for the following, in order:
-;
-; a. !PXE structure as SS:[SP+4]
-; b. PXENV+ structure at [ES:BX]
-; c. INT 1Ah AX=5650h -> PXENV+
-; d. Search memory for !PXE
-; e. Search memory for PXENV+
+; Plan C, because it must be in real mode, we set it here
 ;
-; If we find a PXENV+ structure, we try to find a !PXE structure from
-; it if the API version is 2.1 or later.
-;
-               ; Plan A: !PXE structure as SS:[SP+4]
-               lgs bp,[InitStack]      ; GS:BP -> original stack
-               les bx,[gs:bp+48]
-               call is_pxe
-               je have_pxe
-
-               ; Plan B: PXENV+ structure at [ES:BX]
-               inc byte [plan]
-               mov bx,[gs:bp+24]       ; Original BX
-               mov es,[gs:bp+4]        ; Original ES
-               call is_pxenv
-               je have_pxenv
-
+                global plan_c
+plan_c:
+                
                ; Plan C: PXENV+ structure via INT 1Ah AX=5650h
-               inc byte [plan]
                mov ax, 5650h
 %if USE_PXE_PROVIDED_STACK == 0
                lss sp,[InitStack]
@@ -322,285 +311,10 @@ _start1:
                lss esp,[BaseStack]
 %endif
                sti                     ; Work around Etherboot bug
+                ret
 
-               jc no_int1a
-               cmp ax,564Eh
-               jne no_int1a
-
-               call is_pxenv
-               je have_pxenv
-
-no_int1a:
-               ; Plan D: !PXE memory scan
-               inc byte [plan]
-               call memory_scan_for_pxe_struct         ; !PXE scan
-               je have_pxe
-
-               ; Plan E: PXENV+ memory scan
-               inc byte [plan]
-               call memory_scan_for_pxenv_struct       ; PXENV+ scan
-               je have_pxenv
-
-               ; Found nothing at all!!
-no_pxe:
-               mov si,err_nopxe
-               call writestr_early
-               jmp kaboom
-
-have_pxenv:
-               mov [StrucPtr],bx
-               mov [StrucPtr+2],es
-
-               mov si,found_pxenv
-               call writestr_early
-
-               mov si,apiver_str
-               call writestr_early
-               mov ax,[es:bx+6]
-               mov [APIVer],ax
-               call writehex4
-               call crlf
-
-               cmp ax,0201h                    ; API version 2.1 or higher
-               jb .old_api
-               cmp byte [es:bx+8],2Ch          ; Space for !PXE pointer?
-               jb .pxescan
-               les bx,[es:bx+28h]              ; !PXE structure pointer
-               call is_pxe
-               je have_pxe
-
-               ; Nope, !PXE structure missing despite API 2.1+, or at least
-               ; the pointer is missing.  Do a last-ditch attempt to find it.
-.pxescan:
-               call memory_scan_for_pxe_struct
-               je have_pxe
-
-               ; Otherwise, no dice, use PXENV+ structure
-.old_api:
-               les bx,[StrucPtr]
-               push word [es:bx+22h]           ; UNDI data len
-               push word [es:bx+20h]           ; UNDI data seg
-               push word [es:bx+26h]           ; UNDI code len
-               push word [es:bx+24h]           ; UNDI code seg
-               push dword [es:bx+0Ah]          ; PXENV+ entry point
-
-               mov si,pxenventry_msg
-               jmp have_entrypoint
-
-have_pxe:
-               mov [StrucPtr],bx
-               mov [StrucPtr+2],es
-
-               push word [es:bx+2Eh]           ; UNDI data len
-               push word [es:bx+28h]           ; UNDI data seg
-               push word [es:bx+36h]           ; UNDI code len
-               push word [es:bx+30h]           ; UNDI code seg
-               push dword [es:bx+10h]          ; !PXE entry point
-
-               mov si,pxeentry_msg
-
-have_entrypoint:
-               push cs
-               pop es                          ; Restore CS == DS == ES
-
-               call writestr_early             ; !PXE or PXENV+ entry found
-
-               pop dword [PXEEntry]
-               mov ax,[PXEEntry+2]
-               call writehex4
-               mov al,':'
-               call writechr
-               mov ax,[PXEEntry]
-               call writehex4
-
-               mov si,viaplan_msg
-               call writestr_early
-
-               mov si,undi_code_msg
-               call writestr_early
-               pop ax                          ; UNDI code segment
-               call writehex4
-               xchg dx,ax
-               mov si,len_msg
-               call writestr_early
-               pop ax                          ; UNDI code length
-               call writehex4
-               call crlf
-               add ax,15
-               shr ax,4
-               add dx,ax                       ; DX = seg of end of UNDI code
-
-               mov si,undi_data_msg
-               call writestr_early
-               pop ax                          ; UNDI data segment
-               call writehex4
-               xchg bx,ax
-               mov si,len_msg
-               call writestr_early
-               pop ax                          ; UNDI data length
-               call writehex4
-               call crlf
-               add ax,15
-               shr ax,4
-               add ax,bx                       ; AX = seg of end of UNDI data
-
-               cmp ax,dx
-               ja .data_on_top
-               xchg ax,dx
-.data_on_top:
-               ; Could we safely add 63 here before the shift?
-               shr ax,6                        ; Convert to kilobytes
-               mov [RealBaseMem],ax
-
-
-;
-; Network-specific initialization
-;
-               xor ax,ax
-               mov [LocalDomain],al            ; No LocalDomain received
-
-;
-; The DHCP client identifiers are best gotten from the DHCPREQUEST
-; packet (query info 1).
-;
-query_bootp_1:
-               mov si,get_packet_msg
-               call writestr_early
-
-               mov dl,1
-               call pxe_get_cached_info
-               call parse_dhcp
-
-               ; We don't use flags from the request packet, so
-               ; this is a good time to initialize DHCPMagic...
-               ; Initialize it to 1 meaning we will accept options found;
-               ; in earlier versions of PXELINUX bit 0 was used to indicate
-               ; we have found option 208 with the appropriate magic number;
-               ; we no longer require that, but MAY want to re-introduce
-               ; it in the future for vendor encapsulated options.
-               mov byte [DHCPMagic],1
-
-;
-; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
-; address).  This lives in the DHCPACK packet (query info 2).
-;
-query_bootp_2:
-               mov dl,2
-               call pxe_get_cached_info
-               call parse_dhcp                 ; Parse DHCP packet
-;
-; Save away MAC address (assume this is in query info 2.  If this
-; turns out to be problematic it might be better getting it from
-; the query info 1 packet.)
-;
-.save_mac:
-               movzx cx,byte [trackbuf+bootp.hardlen]
-               cmp cx,16
-               jna .mac_ok
-               xor cx,cx               ; Bad hardware address length
-.mac_ok:
-               mov [MACLen],cl
-               mov al,[trackbuf+bootp.hardware]
-               mov [MACType],al
-               mov si,trackbuf+bootp.macaddr
-               mov di,MAC
-               rep movsb
-
-; Enable this if we really need to zero-pad this field...
-;              mov cx,MAC+MAC_MAX+1
-;              sub cx,di
-;              xor ax,ax
-;              rep stosb
-
-;
-; Now, get the boot file and other info.  This lives in the CACHED_REPLY
-; packet (query info 3).
-;
-query_bootp_3:
-               mov dl,3
-               call pxe_get_cached_info
-               call parse_dhcp                 ; Parse DHCP packet
-               call crlf
-
-;
-; Generate the bootif string, and the hardware-based config string.
-;
-make_bootif_string:
-               mov si,bootif_str
-               mov di,BOOTIFStr
-               mov cx,bootif_str_len
-               rep movsb
-
-               movzx cx,byte [MACLen]
-               mov si,MACType
-               inc cx
-.hexify_mac:
-               push cx
-               mov cl,1                ; CH == 0 already
-               call lchexbytes
-               mov al,'-'
-               stosb
-               pop cx
-               loop .hexify_mac
-               mov [di-1],cl           ; Null-terminate and strip final dash
-;
-; Generate ip= option
-;
-               call genipopt
-
-;
-; Print IP address
-;
-               mov eax,[MyIP]
-               mov di,DotQuadBuf
-               push di
-               call gendotquad                 ; This takes network byte order input
-
-               xchg ah,al                      ; Convert to host byte order
-               ror eax,16                      ; (BSWAP doesn't work on 386)
-               xchg ah,al
-
-               mov si,myipaddr_msg
-               call writestr_early
-               call writehex8
-               mov al,' '
-               call writechr
-               pop si                          ; DotQuadBuf
-               call writestr_early
-               call crlf
-
-               mov si,IPOption
-               call writestr_early
-               call crlf
-
-;
-; Check to see if we got any PXELINUX-specific DHCP options; in particular,
-; if we didn't get the magic enable, do not recognize any other options.
-;
-check_dhcp_magic:
-               test byte [DHCPMagic], 1        ; If we didn't get the magic enable...
-               jnz .got_magic
-               mov byte [DHCPMagic], 0         ; If not, kill all other options
-.got_magic:
-
-
-;
-; Initialize UDP stack
-;
-udp_init:
-               mov eax,[MyIP]
-               mov [pxe_udp_open_pkt.sip],eax
-               mov di,pxe_udp_open_pkt
-               mov bx,PXENV_UDP_OPEN
-               call pxenv
-               jc .failed
-               cmp word [pxe_udp_open_pkt.status], byte 0
-               je .success
-.failed:       mov si,err_udpinit
-               call writestr_early
-               jmp kaboom
-.success:
 
+next:
 ;
 ; Common initialization code
 ;
@@ -627,145 +341,9 @@ udp_init:
 ;
 
 ;
-; Store standard filename prefix
-;
-prefix:                test byte [DHCPMagic], 04h      ; Did we get a path prefix option
-               jnz .got_prefix
-               mov si,BootFile
-               mov di,PathPrefix
-               cld
-               call strcpy
-               mov cx,di
-               sub cx,PathPrefix+1
-               std
-               lea si,[di-2]                   ; Skip final null!
-.find_alnum:   lodsb
-               or al,20h
-               cmp al,'.'                      ; Count . or - as alphanum
-               je .alnum
-               cmp al,'-'
-               je .alnum
-               cmp al,'0'
-               jb .notalnum
-               cmp al,'9'
-               jbe .alnum
-               cmp al,'a'
-               jb .notalnum
-               cmp al,'z'
-               ja .notalnum
-.alnum:                loop .find_alnum
-               dec si
-.notalnum:     mov byte [si+2],0               ; Zero-terminate after delimiter
-               cld
-.got_prefix:
-               mov si,tftpprefix_msg
-               call writestr_early
-               mov si,PathPrefix
-               call writestr_early
-               call crlf
-
-               ; Set CurrentDirName
-               push di
-               mov si,PathPrefix
-               mov di,CurrentDirName
-               call strcpy
-               pop di
-
-;
 ; Load configuration file
 ;
-find_config:
-
-;
-; Begin looking for configuration file
-;
-config_scan:
-               test byte [DHCPMagic], 02h
-               jz .no_option
-
-               ; We got a DHCP option, try it first
-               call .try
-               jnz .success
-
-.no_option:
-               mov di,ConfigName
-               mov si,cfgprefix
-               mov cx,cfgprefix_len
-               rep movsb
-
-               ; Have to guess config file name...
-
-               ; Try loading by UUID.
-               cmp byte [HaveUUID],0
-               je .no_uuid
-
-               push di
-               mov bx,uuid_dashes
-               mov si,UUID
-.gen_uuid:
-               movzx cx,byte [bx]
-               jcxz .done_uuid
-               inc bx
-               call lchexbytes
-               mov al,'-'
-               stosb
-               jmp .gen_uuid
-.done_uuid:
-               mov [di-1],cl           ; Remove last dash and zero-terminate
-               pop di
-               call .try
-               jnz .success
-.no_uuid:
-
-               ; Try loading by MAC address
-               push di
-               mov si,MACStr
-               call strcpy
-               pop di
-               call .try
-               jnz .success
-
-               ; Nope, try hexadecimal IP prefixes...
-.scan_ip:
-               mov cx,4
-               mov si,MyIP
-               call uchexbytes                 ; Convert to hex string
-
-               mov cx,8                        ; Up to 8 attempts
-.tryagain:
-               mov byte [di],0                 ; Zero-terminate string
-               call .try
-               jnz .success
-               dec di                          ; Drop one character
-               loop .tryagain
-
-               ; Final attempt: "default" string
-               mov si,default_str              ; "default" string
-               call strcpy
-               call .try
-               jnz .success
-
-               mov si,err_noconfig
-               call writestr_early
-               jmp kaboom
-
-.try:
-               pusha
-               mov si,trying_msg
-               call writestr_early
-               mov di,ConfigName
-               mov si,di
-               call writestr_early
-               call crlf
-               mov si,di
-               mov di,KernelName       ;  Borrow this buffer for mangled name
-               call mangle_name
-               call open
-               popa
-               ret
-
-
-.success:
+                pm_call load_config
 
 ;
 ; Linux kernel loading code is common.  However, we need to define
@@ -814,6 +392,11 @@ local_boot:
 ; kaboom: write a message and bail out.  Wait for quite a while,
 ;        or a user keypress, then do a hard reboot.
 ;
+                global no_config, kaboom
+; set the no_config kaboom here
+no_config:
+                mov si, err_noconfig
+                call writestr_early
 kaboom:
                RESET_STACK_AND_SEGS AX
 .patch:                mov si,bailmsg
@@ -848,718 +431,6 @@ kaboom:
                mov word [BIOS_magic],0 ; Cold reboot
                jmp 0F000h:0FFF0h       ; Reset vector address
 
-;
-; memory_scan_for_pxe_struct:
-; memory_scan_for_pxenv_struct:
-;
-;      If none of the standard methods find the !PXE/PXENV+ structure,
-;      look for it by scanning memory.
-;
-;      On exit, if found:
-;              ZF = 1, ES:BX -> !PXE structure
-;      Otherwise:
-;              ZF = 0
-;
-;      Assumes DS == CS
-;      Clobbers AX, BX, CX, DX, SI, ES
-;
-memory_scan_for_pxe_struct:
-               mov dx,is_pxe
-               mov ax,[BIOS_fbm]       ; Starting segment
-               shl ax,(10-4)           ; Kilobytes -> paragraphs
-               jmp memory_scan_common
-
-memory_scan_for_pxenv_struct:
-               mov ax,1000h            ; Starting segment
-               mov dx,is_pxenv
-               ; fall through
-
-memory_scan_common:
-               dec ax                  ; To skip inc ax
-.mismatch:
-               inc ax
-               cmp ax,0A000h-1         ; End of memory
-               ja .not_found           ; ZF = 0 on not found
-               mov es,ax
-               xor bx,bx
-               call dx
-               jne .mismatch
-.not_found:
-               ret
-
-;
-; is_pxe:
-;      Validity check on possible !PXE structure in ES:BX
-; is_pxenv:
-;      Validity check on possible PXENV+ structure in ES:BX
-;
-;      Return ZF = 1 on success
-;
-;      Clobbers CX and SI
-;
-is_struc:
-.pxe:
-               cmp dword [es:bx],'!PXE'
-               jne .bad
-               movzx cx,byte [es:bx+4]
-               cmp cx,58h
-               jae .checksum
-               ret
-.pxenv:
-               cmp dword [es:bx],'PXEN'
-               jne .bad
-               cmp word [es:bx+4],'V+'
-               jne .bad
-               movzx cx,[es:bx+8]
-               cmp cx,28h
-               jb .bad
-.checksum:
-               push ax
-               mov si,bx
-               xor ax,ax
-.loop:
-               es lodsb
-               add ah,al
-               loop .loop
-               pop ax
-.bad:
-               ret
-
-is_pxe         equ is_struc.pxe
-is_pxenv       equ is_struc.pxenv
-
-;
-; close_file:
-;           Deallocates a file structure (pointer in SI)
-;           Assumes CS == DS.
-;
-; XXX: We should check to see if this file is still open on the server
-; side and send a courtesy ERROR packet to the server.
-;
-close_file:
-               and si,si
-               jz .closed
-               mov word [si],0         ; Not in use
-.closed:       ret
-
-;
-; searchdir:
-;
-;      Open a TFTP connection to the server
-;
-;           On entry:
-;              DS:DI   = mangled filename
-;           If successful:
-;              ZF clear
-;              SI      = socket pointer
-;              EAX     = file length in bytes, or -1 if unknown
-;           If unsuccessful
-;              ZF set
-;
-
-searchdir:
-               push es
-               push bx
-               push cx
-               mov ax,ds
-               mov es,ax
-               mov si,di
-               push bp
-               mov bp,sp
-
-               call allocate_socket
-               jz .ret
-
-               mov ax,TimeoutTable     ; Reset timeout
-
-.sendreq:      push ax                 ; [bp-2]  - Timeout pointer
-               push si                 ; [bp-4]  - File name
-
-               mov di,packet_buf
-               mov [pxe_udp_write_pkt.buffer],di
-
-               mov ax,TFTP_RRQ         ; TFTP opcode
-               stosw
-
-               lodsd                   ; EAX <- server override (if any)
-               and eax,eax
-               jnz .noprefix           ; No prefix, and we have the server
-
-               push si                 ; Add common prefix
-               mov si,PathPrefix
-               call strcpy
-               dec di
-               pop si
-
-               mov eax,[ServerIP]      ; Get default server
-
-.noprefix:
-               call strcpy             ; Filename
-%if GPXE
-               mov si,packet_buf+2
-               call is_gpxe
-               jnc .gpxe
-%endif
-
-               mov [bx+tftp_remoteip],eax
-
-               push bx                 ; [bp-6]  - TFTP block
-               mov bx,[bx]
-               push bx                 ; [bp-8]  - TID (local port no)
-
-               mov [pxe_udp_write_pkt.sip],eax
-               ; Now figure out the gateway
-               xor eax,[MyIP]
-               and eax,[Netmask]
-               jz .nogwneeded
-               mov eax,[Gateway]
-.nogwneeded:
-               mov [pxe_udp_write_pkt.gip],eax
-               mov [pxe_udp_write_pkt.lport],bx
-               mov ax,[ServerPort]
-               mov [pxe_udp_write_pkt.rport],ax
-               mov si,tftp_tail
-               mov cx,tftp_tail_len
-               rep movsb
-               sub di,packet_buf       ; Get packet size
-               mov [pxe_udp_write_pkt.buffersize],di
-
-               mov di,pxe_udp_write_pkt
-               mov bx,PXENV_UDP_WRITE
-               call pxenv
-               jc .failure
-               cmp word [pxe_udp_write_pkt.status],byte 0
-               jne .failure
-
-               ;
-               ; Danger, Will Robinson!  We need to support timeout
-               ; and retry lest we just lost a packet...
-               ;
-
-               ; Packet transmitted OK, now we need to receive
-.getpacket:    mov bx,[bp-2]
-               movzx bx,byte [bx]
-               push bx                 ; [bp-10] - timeout in ticks
-               push word [BIOS_timer]  ; [bp-12]
-
-.pkt_loop:     mov bx,[bp-8]           ; TID
-               mov di,packet_buf
-               mov [pxe_udp_read_pkt.buffer],di
-               mov [pxe_udp_read_pkt.buffer+2],ds
-               mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
-               mov eax,[MyIP]
-               mov [pxe_udp_read_pkt.dip],eax
-               mov [pxe_udp_read_pkt.lport],bx
-               mov di,pxe_udp_read_pkt
-               mov bx,PXENV_UDP_READ
-               call pxenv
-               jnc .got_packet                 ; Wait for packet
-.no_packet:
-               mov dx,[BIOS_timer]
-               cmp dx,[bp-12]
-               je .pkt_loop
-               mov [bp-12],dx
-               dec word [bp-10]
-               jnz .pkt_loop
-               pop ax  ; Adjust stack
-               pop ax
-               jmp .failure
-
-.got_packet:
-               mov si,[bp-6]                   ; TFTP pointer
-               mov bx,[bp-8]                   ; TID
-
-               ; Make sure the packet actually came from the server
-               ; This is technically not to the TFTP spec?
-               mov eax,[si+tftp_remoteip]
-               cmp [pxe_udp_read_pkt.sip],eax
-               jne .no_packet
-
-               ; Got packet - reset timeout
-               mov word [bp-2],TimeoutTable
-
-               pop ax  ; Adjust stack
-               pop ax
-
-               mov ax,[pxe_udp_read_pkt.rport]
-               mov [si+tftp_remoteport],ax
-
-               ; filesize <- -1 == unknown
-               mov dword [si+tftp_filesize], -1
-               ; Default blksize unless blksize option negotiated
-               mov word [si+tftp_blksize], TFTP_BLOCKSIZE
-
-               movzx ecx,word [pxe_udp_read_pkt.buffersize]
-               sub cx,2                ; CX <- bytes after opcode
-               jb .failure             ; Garbled reply
-
-               mov si,packet_buf
-               lodsw
-
-               cmp ax, TFTP_ERROR
-               je .bailnow             ; ERROR reply: don't try again
-
-               ; If the server doesn't support any options, we'll get
-               ; a DATA reply instead of OACK.  Stash the data in
-               ; the file buffer and go with the default value for
-               ; all options...
-               cmp ax, TFTP_DATA
-               je .no_oack
-
-               cmp ax, TFTP_OACK
-               jne .err_reply          ; Unknown packet type
-
-               ; Now we need to parse the OACK packet to get the transfer
-               ; and packet sizes.
-               ;  SI -> first byte of options; [E]CX -> byte count
-.parse_oack:
-               jcxz .done_pkt                  ; No options acked
-.get_opt_name:
-               mov di,si
-               mov bx,si
-.opt_name_loop:        lodsb
-               and al,al
-               jz .got_opt_name
-               or al,20h                       ; Convert to lowercase
-               stosb
-               loop .opt_name_loop
-               ; We ran out, and no final null
-               jmp .err_reply
-.got_opt_name: ; si -> option value
-               dec cx                          ; bytes left in pkt
-               jz .err_reply                   ; Option w/o value
-
-               ; Parse option pointed to by bx; guaranteed to be
-               ; null-terminated.
-               push cx
-               push si
-               mov si,bx                       ; -> option name
-               mov bx,tftp_opt_table
-               mov cx,tftp_opts
-.opt_loop:
-               push cx
-               push si
-               mov di,[bx]                     ; Option pointer
-               mov cx,[bx+2]                   ; Option len
-               repe cmpsb
-               pop si
-               pop cx
-               je .get_value                   ; OK, known option
-               add bx,6
-               loop .opt_loop
-
-               pop si
-               pop cx
-               jmp .err_reply                  ; Non-negotiated option returned
-
-.get_value:    pop si                          ; si -> option value
-               pop cx                          ; cx -> bytes left in pkt
-               mov bx,[bx+4]                   ; Pointer to data target
-               add bx,[bp-6]                   ; TFTP socket pointer
-               xor eax,eax
-               xor edx,edx
-.value_loop:   lodsb
-               and al,al
-               jz .got_value
-               sub al,'0'
-               cmp al, 9
-               ja .err_reply                   ; Not a decimal digit
-               imul edx,10
-               add edx,eax
-               mov [bx],edx
-               loop .value_loop
-               ; Ran out before final null, accept anyway
-               jmp short .done_pkt
-
-.got_value:
-               dec cx
-               jnz .get_opt_name               ; Not end of packet
-
-               ; ZF == 1
-
-               ; Success, done!
-.done_pkt:
-               pop si                  ; Junk
-               pop si                  ; We want the packet ptr in SI
-
-               mov eax,[si+tftp_filesize]
-.got_file:                             ; SI->socket structure, EAX = size
-               and eax,eax             ; Set ZF depending on file size
-               jz .error_si            ; ZF = 1 need to free the socket
-.ret:
-               leave                   ; SP <- BP, POP BP
-               pop cx
-               pop bx
-               pop es
-               ret
-
-
-.no_oack:      ; We got a DATA packet, meaning no options are
-               ; suported.  Save the data away and consider the length
-               ; undefined, *unless* this is the only data packet...
-               mov bx,[bp-6]           ; File pointer
-               sub cx,2                ; Too short?
-               jb .failure
-               lodsw                   ; Block number
-               cmp ax,htons(1)
-               jne .failure
-               mov [bx+tftp_lastpkt],ax
-               cmp cx,TFTP_BLOCKSIZE
-               ja .err_reply           ; Corrupt...
-               je .not_eof
-               ; This was the final EOF packet, already...
-               ; We know the filesize, but we also want to ack the
-               ; packet and set the EOF flag.
-               mov [bx+tftp_filesize],ecx
-               mov byte [bx+tftp_goteof],1
-               push si
-               mov si,bx
-               ; AX = htons(1) already
-               call ack_packet
-               pop si
-.not_eof:
-               mov [bx+tftp_bytesleft],cx
-               mov ax,pktbuf_seg
-               push es
-               mov es,ax
-               mov di,tftp_pktbuf
-               mov [bx+tftp_dataptr],di
-               add cx,3
-               shr cx,2
-               rep movsd
-               pop es
-               jmp .done_pkt
-
-.err_reply:    ; Option negotiation error.  Send ERROR reply.
-               ; ServerIP and gateway are already programmed in
-               mov si,[bp-6]
-               mov ax,[si+tftp_remoteport]
-               mov word [pxe_udp_write_pkt.rport],ax
-               mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
-               mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
-               mov di,pxe_udp_write_pkt
-               mov bx,PXENV_UDP_WRITE
-               call pxenv
-
-               ; Write an error message and explode
-               mov si,err_damage
-               call writestr_early
-               jmp kaboom
-
-.bailnow:
-               ; Immediate error - no retry
-               mov word [bp-2],TimeoutTableEnd-1
-
-.failure:      pop bx                  ; Junk
-               pop bx
-               pop si
-               pop ax
-               inc ax
-               cmp ax,TimeoutTableEnd
-               jb .sendreq             ; Try again
-
-.error:                mov si,bx               ; Socket pointer
-.error_si:                             ; Socket pointer already in SI
-               call free_socket        ; ZF <- 1, SI <- 0
-               jmp .ret
-
-
-%if GPXE
-.gpxe:
-               push bx                 ; Socket pointer
-               mov di,gpxe_file_open
-               mov word [di],2         ; PXENV_STATUS_BAD_FUNC
-               mov word [di+4],packet_buf+2    ; Completed URL
-               mov [di+6],ds
-               mov bx,PXENV_FILE_OPEN
-               call pxenv
-               pop si                  ; Socket pointer in SI
-               jc .error_si
-
-               mov ax,[di+2]
-               mov word [si+tftp_localport],-1 ; gPXE URL
-               mov [si+tftp_remoteport],ax
-               mov di,gpxe_get_file_size
-               mov [di+2],ax
-
-%if 0
-               ; Disable this for now since gPXE doesn't always
-               ; return valid information in PXENV_GET_FILE_SIZE
-               mov bx,PXENV_GET_FILE_SIZE
-               call pxenv
-               mov eax,[di+4]          ; File size
-               jnc .oksize
-%endif
-               or eax,-1               ; Size unknown
-.oksize:
-               mov [si+tftp_filesize],eax
-               jmp .got_file
-%endif ; GPXE
-
-;
-; allocate_socket: Allocate a local UDP port structure
-;
-;              If successful:
-;                ZF set
-;                BX     = socket pointer
-;               If unsuccessful:
-;                 ZF clear
-;
-allocate_socket:
-               push cx
-               mov bx,Files
-               mov cx,MAX_OPEN
-.check:                cmp word [bx], byte 0
-               je .found
-               add bx,open_file_t_size
-               loop .check
-               xor cx,cx                       ; ZF = 1
-               pop cx
-               ret
-               ; Allocate a socket number.  Socket numbers are made
-               ; guaranteed unique by including the socket slot number
-               ; (inverted, because we use the loop counter cx); add a
-               ; counter value to keep the numbers from being likely to
-               ; get immediately reused.
-               ;
-               ; The NextSocket variable also contains the top two bits
-               ; set.  This generates a value in the range 49152 to
-               ; 57343.
-.found:
-               dec cx
-               push ax
-               mov ax,[NextSocket]
-               inc ax
-               and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
-               mov [NextSocket],ax
-               shl cx,13-MAX_OPEN_LG2
-               add cx,ax                       ; ZF = 0
-               xchg ch,cl                      ; Convert to network byte order
-               mov [bx],cx                     ; Socket in use
-               pop ax
-               pop cx
-               ret
-
-;
-; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
-;
-free_socket:
-               push es
-               pusha
-               xor ax,ax
-               mov es,ax
-               mov di,si
-               mov cx,tftp_pktbuf >> 1         ; tftp_pktbuf is not cleared
-               rep stosw
-               popa
-               pop es
-               xor si,si
-               ret
-
-;
-; parse_dotquad:
-;              Read a dot-quad pathname in DS:SI and output an IP
-;              address in EAX, with SI pointing to the first
-;              nonmatching character.
-;
-;              Return CF=1 on error.
-;
-;              No segment assumptions permitted.
-;
-parse_dotquad:
-               push cx
-               mov cx,4
-               xor eax,eax
-.parseloop:
-               mov ch,ah
-               mov ah,al
-               lodsb
-               sub al,'0'
-               jb .notnumeric
-               cmp al,9
-               ja .notnumeric
-               aad                             ; AL += 10 * AH; AH = 0;
-               xchg ah,ch
-               jmp .parseloop
-.notnumeric:
-               cmp al,'.'-'0'
-               pushf
-               mov al,ah
-               mov ah,ch
-               xor ch,ch
-               ror eax,8
-               popf
-               jne .error
-               loop .parseloop
-               jmp .done
-.error:
-               loop .realerror                 ; If CX := 1 then we're done
-               clc
-               jmp .done
-.realerror:
-               stc
-.done:
-               dec si                          ; CF unchanged!
-               pop cx
-               ret
-
-;
-; is_url:      Return CF=0 if and only if the buffer pointed to by
-;             DS:SI is a URL (contains ://).  No registers modified.
-;
-%if GPXE
-is_url:
-               push si
-               push eax
-.loop:
-               mov eax,[si]
-               inc si
-               and al,al
-               jz .not_url
-               and eax,0FFFFFFh
-               cmp eax,'://'
-               jne .loop
-.done:
-               ; CF=0 here
-               pop eax
-               pop si
-               ret
-.not_url:
-               stc
-               jmp .done
-
-;
-; is_gpxe:     Return CF=0 if and only if the buffer pointed to by
-;             DS:SI is a URL (contains ://) *and* the gPXE extensions
-;             API is available.  No registers modified.
-;
-is_gpxe:
-               call is_url
-               jc .ret                 ; Not a URL, don't bother
-.again:
-               cmp byte [HasGPXE],1
-               ja .unknown
-               ; CF=1 if not available (0),
-               ; CF=0 if known available (1).
-.ret:          ret
-
-.unknown:
-               ; If we get here, the gPXE status is unknown.
-               push es
-               pushad
-               push ds
-               pop es
-               mov di,gpxe_file_api_check
-               mov bx,PXENV_FILE_API_CHECK     ; BH = 0
-               call pxenv
-               jc .nogood
-               cmp dword [di+4],0xe9c17b20
-               jne .nogood
-               mov ax,[di+12]          ; Don't care about the upper half...
-               not ax                  ; Set bits of *missing* functions...
-               and ax,01001011b        ; The functions we care about
-               setz bh
-               jz .done
-.nogood:
-               mov si,gpxe_warning_msg
-               call writestr_early
-.done:
-               mov [HasGPXE],bh
-               popad
-               pop es
-               jmp .again
-
-               section .data16
-gpxe_warning_msg:
-               db 'URL syntax, but gPXE extensions not detected, '
-               db 'trying plain TFTP...', CR, LF, 0
-HasGPXE                db -1                   ; Unknown
-               section .text16
-
-%endif
-
-;
-; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
-;             to by ES:DI; ends on encountering any whitespace.
-;             DI is preserved.
-;
-;             This verifies that a filename is < FILENAME_MAX characters
-;             and doesn't contain whitespace, and zero-pads the output buffer,
-;             so "repe cmpsb" can do a compare.
-;
-;             The first four bytes of the manged name is the IP address of
-;             the download host, 0 for no host, or -1 for a gPXE URL.
-;
-;              No segment assumptions permitted.
-;
-mangle_name:
-               push di
-%if GPXE
-               call is_url
-               jc .not_url
-               or eax,-1                       ; It's a URL
-               jmp .prefix_done
-.not_url:
-%endif ; GPXE
-               push si
-               mov eax,[cs:ServerIP]
-               cmp byte [si],0
-               je .noip                        ; Null filename?!?!
-               cmp word [si],'::'              ; Leading ::?
-               je .gotprefix
-
-.more:
-               inc si
-               cmp byte [si],0
-               je .noip
-               cmp word [si],'::'
-               jne .more
-
-               ; We have a :: prefix of some sort, it could be either
-               ; a DNS name or a dot-quad IP address.  Try the dot-quad
-               ; first...
-.here:
-               pop si
-               push si
-               call parse_dotquad
-               jc .notdq
-               cmp word [si],'::'
-               je .gotprefix
-.notdq:
-               pop si
-               push si
-               call dns_resolv
-               cmp word [si],'::'
-               jne .noip
-               and eax,eax
-               jnz .gotprefix
-
-.noip:
-               pop si
-               xor eax,eax
-               jmp .prefix_done
-
-.gotprefix:
-               pop cx                          ; Adjust stack
-               inc si                          ; Skip double colon
-               inc si
-
-.prefix_done:
-               stosd                           ; Save IP address prefix
-               mov cx,FILENAME_MAX-5
-
-.mn_loop:
-               lodsb
-               cmp al,' '                      ; If control or space, end
-               jna .mn_end
-               stosb
-               loop .mn_loop
-.mn_end:
-               inc cx                          ; At least one null byte
-               xor ax,ax                       ; Zero-fill name
-               rep stosb                       ; Doesn't do anything if CX=0
-               pop di
-               ret                             ; Done
 
 ;
 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
@@ -1582,7 +453,7 @@ unmangle_name:
                jz .noip
                cmp eax,-1
                jz .noip                        ; URL
-               call gendotquad
+               pm_call gendotquad
                mov ax,'::'
                stosw
 .noip:
@@ -1602,6 +473,7 @@ unmangle_name:
 ;
 ; While we're at it, save and restore all registers.
 ;
+                global pxenv
 pxenv:
                pushfd
                pushad
@@ -1636,250 +508,13 @@ pxenv:
                ret
 
 ; Must be after function def due to NASM bug
+                global PXEEntry
 PXEEntry       equ pxenv.jump+1
 
                section .bss16
                alignb 2
 PXEStatus      resb 2
 
-               section .text16
-
-;
-; getfssec: Get multiple clusters from a file, given the starting cluster.
-;
-;      In this case, get multiple blocks from a specific TCP connection.
-;
-;  On entry:
-;      ES:BX   -> Buffer
-;      SI      -> TFTP socket pointer
-;      CX      -> 512-byte block count; 0FFFFh = until end of file
-;  On exit:
-;      SI      -> TFTP socket pointer (or 0 on EOF)
-;      CF = 1  -> Hit EOF
-;      ECX     -> number of bytes actually read
-;
-getfssec:
-               push eax
-               push edi
-               push bx
-               push si
-               push fs
-               mov di,bx
-               mov ax,pktbuf_seg
-               mov fs,ax
-
-               xor eax,eax
-               movzx ecx,cx
-               shl ecx,TFTP_BLOCKSIZE_LG2      ; Convert to bytes
-               push ecx                        ; Initial request size
-               jz .hit_eof                     ; Nothing to do?
-
-.need_more:
-               call fill_buffer
-               movzx eax,word [si+tftp_bytesleft]
-               and ax,ax
-               jz .hit_eof
-
-               push ecx
-               cmp ecx,eax
-               jna .ok_size
-               mov ecx,eax
-.ok_size:
-               mov ax,cx                       ; EAX<31:16> == ECX<31:16> == 0
-               mov bx,[si+tftp_dataptr]
-               sub [si+tftp_bytesleft],cx
-               xchg si,bx
-               fs rep movsb                    ; Copy from packet buffer
-               xchg si,bx
-               mov [si+tftp_dataptr],bx
-
-               pop ecx
-               sub ecx,eax
-               jnz .need_more
-
-.hit_eof:
-               call fill_buffer
-
-               pop eax                         ; Initial request amount
-               xchg eax,ecx
-               sub ecx,eax                     ; ... minus anything not gotten
-
-               pop fs
-               pop si
-
-               ; Is there anything left of this?
-               mov eax,[si+tftp_filesize]
-               sub eax,[si+tftp_filepos]
-               jnz .bytes_left
-
-               cmp [si+tftp_bytesleft],ax      ; AX == 0
-               jne .bytes_left
-
-               cmp byte [si+tftp_goteof],0
-               je .done
-               ; I'm 99% sure this can't happen, but...
-               call fill_buffer                ; Receive/ACK the EOF packet
-.done:
-               ; The socket is closed and the buffer drained
-               ; Close socket structure and re-init for next user
-               call free_socket
-               stc
-               jmp .ret
-.bytes_left:
-               clc
-.ret:
-               pop bx
-               pop edi
-               pop eax
-               ret
-
-;
-; Get a fresh packet if the buffer is drained, and we haven't hit
-; EOF yet.  The buffer should be filled immediately after draining!
-;
-; expects fs -> pktbuf_seg and ds:si -> socket structure
-;
-fill_buffer:
-               cmp word [si+tftp_bytesleft],0
-               je .empty
-               ret                             ; Otherwise, nothing to do
-
-.empty:
-               push es
-               pushad
-               mov ax,ds
-               mov es,ax
-
-               ; Note: getting the EOF packet is not the same thing
-               ; as tftp_filepos == tftp_filesize; if the EOF packet
-               ; is empty the latter condition can be true without
-               ; having gotten the official EOF.
-               cmp byte [si+tftp_goteof],0
-               jne .ret                        ; Already EOF
-
-%if GPXE
-               cmp word [si+tftp_localport], -1
-               jne .get_packet_tftp
-               call get_packet_gpxe
-               jmp .ret
-.get_packet_tftp:
-%endif ; GPXE
-
-               ; TFTP code...
-.packet_loop:
-               ; Start by ACKing the previous packet; this should cause the
-               ; next packet to be sent.
-               mov bx,TimeoutTable
-
-.send_ack:     push bx                         ; <D> Retry pointer
-               movzx cx,byte [bx]              ; Timeout
-
-               mov ax,[si+tftp_lastpkt]
-               call ack_packet                 ; Send ACK
-
-               ; We used to test the error code here, but sometimes
-               ; PXE would return negative status even though we really
-               ; did send the ACK.  Now, just treat a failed send as
-               ; a normally lost packet, and let it time out in due
-               ; course of events.
-
-.send_ok:      ; Now wait for packet.
-               mov dx,[BIOS_timer]             ; Get current time
-
-.wait_data:    push cx                         ; <E> Timeout
-               push dx                         ; <F> Old time
-
-               mov bx,[si+tftp_pktbuf]
-               mov [pxe_udp_read_pkt.buffer],bx
-               mov [pxe_udp_read_pkt.buffer+2],fs
-               mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
-               mov eax,[si+tftp_remoteip]
-               mov [pxe_udp_read_pkt.sip],eax
-               mov eax,[MyIP]
-               mov [pxe_udp_read_pkt.dip],eax
-               mov ax,[si+tftp_remoteport]
-               mov [pxe_udp_read_pkt.rport],ax
-               mov ax,[si+tftp_localport]
-               mov [pxe_udp_read_pkt.lport],ax
-               mov di,pxe_udp_read_pkt
-               mov bx,PXENV_UDP_READ
-               call pxenv
-               jnc .recv_ok
-
-               ; No packet, or receive failure
-               mov dx,[BIOS_timer]
-               pop ax                          ; <F> Old time
-               pop cx                          ; <E> Timeout
-               cmp ax,dx                       ; Same time -> don't advance timeout
-               je .wait_data                   ; Same clock tick
-               loop .wait_data                 ; Decrease timeout
-
-               pop bx                          ; <D> Didn't get any, send another ACK
-               inc bx
-               cmp bx,TimeoutTableEnd
-               jb .send_ack
-               jmp kaboom                      ; Forget it...
-
-.recv_ok:      pop dx                          ; <F>
-               pop cx                          ; <E>
-
-               cmp word [pxe_udp_read_pkt.buffersize],byte 4
-               jb .wait_data                   ; Bad size for a DATA packet
-
-               mov bx,[si+tftp_pktbuf]
-               cmp word [fs:bx],TFTP_DATA      ; Not a data packet?
-               jne .wait_data                  ; Then wait for something else
-
-               mov ax,[si+tftp_lastpkt]
-               xchg ah,al                      ; Host byte order
-               inc ax                          ; Which packet are we waiting for?
-               xchg ah,al                      ; Network byte order
-               cmp [fs:bx+2],ax
-               je .right_packet
-
-               ; Wrong packet, ACK the packet and then try again
-               ; This is presumably because the ACK got lost,
-               ; so the server just resent the previous packet
-               mov ax,[fs:bx+2]
-               call ack_packet
-               jmp .send_ok                    ; Reset timeout
-
-.right_packet: ; It's the packet we want.  We're also EOF if the
-               ; size < blocksize
-
-               pop cx                          ; <D> Don't need the retry count anymore
-
-               mov [si+tftp_lastpkt],ax        ; Update last packet number
-
-               movzx ecx,word [pxe_udp_read_pkt.buffersize]
-               sub cx,byte 4                   ; Skip TFTP header
-
-               ; Set pointer to data block
-               lea ax,[bx+4]                   ; Data past TFTP header
-               mov [si+tftp_dataptr],ax
-
-               add [si+tftp_filepos],ecx
-               mov [si+tftp_bytesleft],cx
-
-               cmp cx,[si+tftp_blksize]        ; Is it a full block?
-               jb .last_block                  ; If not, it's EOF
-
-.ret:
-               popad
-               pop es
-               ret
-
-
-.last_block:   ; Last block - ACK packet immediately
-               mov ax,[fs:bx+2]
-               call ack_packet
-
-               ; Make sure we know we are at end of file
-               mov eax,[si+tftp_filepos]
-               mov [si+tftp_filesize],eax
-               mov byte [si+tftp_goteof],1
-
-               jmp .ret
 
 ;
 ; TimeoutTable: list of timeouts (in 18.2 Hz timer ticks)
@@ -1894,96 +529,7 @@ TimeoutTable:
 TimeoutTableEnd        equ $
 
                section .text16
-;
-; ack_packet:
-;
-; Send ACK packet.  This is a common operation and so is worth canning.
-;
-; Entry:
-;      SI      = TFTP block
-;      AX      = Packet # to ack (network byte order)
-; Exit:
-;      All registers preserved
-;
-; This function uses the pxe_udp_write_pkt but not the packet_buf.
-;
-ack_packet:
-               pushad
-               mov [ack_packet_buf+2],ax       ; Packet number to ack
-               mov ax,[si]
-               mov [pxe_udp_write_pkt.lport],ax
-               mov ax,[si+tftp_remoteport]
-               mov [pxe_udp_write_pkt.rport],ax
-               mov eax,[si+tftp_remoteip]
-               mov [pxe_udp_write_pkt.sip],eax
-               xor eax,[MyIP]
-               and eax,[Netmask]
-               jz .nogw
-               mov eax,[Gateway]
-.nogw:
-               mov [pxe_udp_write_pkt.gip],eax
-               mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
-               mov [pxe_udp_write_pkt.buffersize], word 4
-               mov di,pxe_udp_write_pkt
-               mov bx,PXENV_UDP_WRITE
-               call pxenv
-               popad
-               ret
-
-%if GPXE
-;
-; Get a fresh packet from a gPXE socket; expects fs -> pktbuf_seg
-; and ds:si -> socket structure
-;
-; Assumes CS == DS == ES.
-;
-get_packet_gpxe:
-               mov di,gpxe_file_read
-
-               mov ax,[si+tftp_remoteport]     ; gPXE filehandle
-               mov [di+2],ax
-               mov ax,[si+tftp_pktbuf]
-               mov [di+6],ax
-               mov [si+tftp_dataptr],ax
-               mov [di+8],fs
-
-.again:
-               mov word [di+4],PKTBUF_SIZE
-               mov bx,PXENV_FILE_READ
-               call pxenv
-               jnc .ok                         ; Got data or EOF
-               cmp word [di],PXENV_STATUS_TFTP_OPEN    ; == EWOULDBLOCK
-               je .again
-               jmp kaboom                      ; Otherwise error...
-
-.ok:
-               movzx eax,word [di+4]           ; Bytes read
-               mov [si+tftp_bytesleft],ax      ; Bytes in buffer
-               add [si+tftp_filepos],eax       ; Position in file
-
-               and ax,ax                       ; EOF?
-               mov eax,[si+tftp_filepos]
-
-               jnz .got_stuff
-
-               ; We got EOF here, make sure the upper layers know
-               mov [si+tftp_filesize],eax
 
-.got_stuff:
-               ; If we're done here, close the file
-               cmp [si+tftp_filesize],eax
-               ja .done                ; Not EOF, there is still data...
-
-               ; Reuse the previous [es:di] structure since the
-               ; relevant fields are all the same
-               mov byte [si+tftp_goteof],1
-
-               mov bx,PXENV_FILE_CLOSE
-               call pxenv
-               ; Ignore return...
-.done:
-               ret
-%endif ; GPXE
 
 ;
 ; unload_pxe:
@@ -2082,460 +628,7 @@ reset_pxe:
                pop es
                ret
 
-;
-; gendotquad
-;
-; Take an IP address (in network byte order) in EAX and
-; output a dotted quad string to ES:DI.
-; DI points to terminal null at end of string on exit.
-;
-gendotquad:
-               push eax
-               push cx
-               mov cx,4
-.genchar:
-               push eax
-               cmp al,10               ; < 10?
-               jb .lt10                ; If so, skip first 2 digits
-
-               cmp al,100              ; < 100
-               jb .lt100               ; If so, skip first digit
-
-               aam 100
-               ; Now AH = 100-digit; AL = remainder
-               add ah,'0'
-               mov [es:di],ah
-               inc di
-
-.lt100:
-               aam 10
-               ; Now AH = 10-digit; AL = remainder
-               add ah,'0'
-               mov [es:di],ah
-               inc di
-
-.lt10:
-               add al,'0'
-               stosb
-               mov al,'.'
-               stosb
-               pop eax
-               ror eax,8       ; Move next char into LSB
-               loop .genchar
-               dec di
-               mov [es:di], byte 0
-               pop cx
-               pop eax
-               ret
-;
-; uchexbytes/lchexbytes
-;
-; Take a number of bytes in memory and convert to upper/lower-case
-; hexadecimal
-;
-; Input:
-;      DS:SI   = input bytes
-;      ES:DI   = output buffer
-;      CX      = number of bytes
-; Output:
-;      DS:SI   = first byte after
-;      ES:DI   = first byte after
-;      CX = 0
-;
-; Trashes AX, DX
-;
-
-lchexbytes:
-       mov dl,'a'-'9'-1
-       jmp xchexbytes
-uchexbytes:
-       mov dl,'A'-'9'-1
-xchexbytes:
-.loop:
-       lodsb
-       mov ah,al
-       shr al,4
-       call .outchar
-       mov al,ah
-       call .outchar
-       loop .loop
-       ret
-.outchar:
-       and al,0Fh
-       add al,'0'
-       cmp al,'9'
-       jna .done
-       add al,dl
-.done:
-       stosb
-       ret
-
-;
-; pxe_get_cached_info
-;
-; Get a DHCP packet from the PXE stack into the trackbuf.
-;
-; Input:
-;      DL = packet type
-; Output:
-;      CX = buffer size
-;
-; Assumes CS == DS == ES.
-;
-pxe_get_cached_info:
-               pushad
-               mov al,' '
-               call writechr
-               mov al,dl
-               call writehex2
-               mov di,pxe_bootp_query_pkt
-               push di
-               xor ax,ax
-               stosw           ; Status
-               movzx ax,dl
-               stosw           ; Packet type
-               mov ax,trackbufsize
-               stosw           ; Buffer size
-               mov ax,trackbuf
-               stosw           ; Buffer offset
-               xor ax,ax
-               stosw           ; Buffer segment
 
-               pop di          ; DI -> parameter set
-               mov bx,PXENV_GET_CACHED_INFO
-               call pxenv
-               jc .err
-
-               popad
-               mov cx,[pxe_bootp_query_pkt.buffersize]
-               ret
-
-.err:
-               mov si,err_pxefailed
-               call writestr_early
-               call writehex4
-               call crlf
-               jmp kaboom
-
-               section .data16
-get_packet_msg db 'Getting cached packet', 0
-
-               section .text16
-;
-; ip_ok
-;
-; Tests an IP address in EAX for validity; return with ZF=1 for bad.
-; We used to refuse class E, but class E addresses are likely to become
-; assignable unicast addresses in the near future.
-;
-ip_ok:
-               push ax
-               cmp eax,-1              ; Refuse the all-ones address
-               jz .out
-               and al,al               ; Refuse network zero
-               jz .out
-               cmp al,127              ; Refuse loopback
-               jz .out
-               and al,0F0h
-               cmp al,224              ; Refuse class D
-.out:
-               pop ax
-               ret
-
-;
-; parse_dhcp
-;
-; Parse a DHCP packet.  This includes dealing with "overloaded"
-; option fields (see RFC 2132, section 9.3)
-;
-; This should fill in the following global variables, if the
-; information is present:
-;
-; MyIP         - client IP address
-; ServerIP     - boot server IP address
-; Netmask      - network mask
-; Gateway      - default gateway router IP
-; BootFile     - boot file name
-; DNSServers   - DNS server IPs
-; LocalDomain  - Local domain name
-; MACLen, MAC  - Client identifier, if MACLen == 0
-;
-; This assumes the DHCP packet is in "trackbuf" and the length
-; of the packet in in CX on entry.
-;
-
-parse_dhcp:
-               mov byte [OverLoad],0           ; Assume no overload
-               mov eax, [trackbuf+bootp.yip]
-               call ip_ok
-               jz .noyip
-               mov [MyIP], eax
-.noyip:
-               mov eax, [trackbuf+bootp.sip]
-               and eax, eax
-               call ip_ok
-               jz .nosip
-               mov [ServerIP], eax
-.nosip:
-               sub cx, bootp.options
-               jbe .nooptions
-               mov si, trackbuf+bootp.option_magic
-               lodsd
-               cmp eax, BOOTP_OPTION_MAGIC
-               jne .nooptions
-               call parse_dhcp_options
-.nooptions:
-               mov si, trackbuf+bootp.bootfile
-               test byte [OverLoad],1
-               jz .nofileoverload
-               mov cx,128
-               call parse_dhcp_options
-               jmp short .parsed_file
-.nofileoverload:
-               cmp byte [si], 0
-               jz .parsed_file                 ; No bootfile name
-               mov di,BootFile
-               mov cx,32
-               rep movsd
-               xor al,al
-               stosb                           ; Null-terminate
-.parsed_file:
-               mov si, trackbuf+bootp.sname
-               test byte [OverLoad],2
-               jz .nosnameoverload
-               mov cx,64
-               call parse_dhcp_options
-.nosnameoverload:
-               ret
-
-;
-; Parse a sequence of DHCP options, pointed to by DS:SI; the field
-; size is CX -- some DHCP servers leave option fields unterminated
-; in violation of the spec.
-;
-; For parse_some_dhcp_options, DH contains the minimum value for
-; the option to recognize -- this is used to restrict parsing to
-; PXELINUX-specific options only.
-;
-parse_dhcp_options:
-               xor dx,dx
-
-parse_some_dhcp_options:
-.loop:
-               and cx,cx
-               jz .done
-
-               lodsb
-               dec cx
-               jz .done        ; Last byte; must be PAD, END or malformed
-               cmp al, 0       ; PAD option
-               je .loop
-               cmp al,255      ; END option
-               je .done
-
-               ; Anything else will have a length field
-               mov dl,al       ; DL <- option number
-               xor ax,ax
-               lodsb           ; AX <- option length
-               dec cx
-               sub cx,ax       ; Decrement bytes left counter
-               jb .done        ; Malformed option: length > field size
-
-               cmp dl,dh       ; Is the option value valid?
-               jb .opt_done
-
-               mov bx,dhcp_option_list
-.find_option:
-               cmp bx,dhcp_option_list_end
-               jae .opt_done
-               cmp dl,[bx]
-               je .found_option
-               add bx,3
-               jmp .find_option
-.found_option:
-               pushad
-               call [bx+1]
-               popad
-
-; Fall through
-               ; Unknown option.  Skip to the next one.
-.opt_done:
-               add si,ax
-               jmp .loop
-.done:
-               ret
-
-               section .data16
-dhcp_option_list:
-               section .text16
-
-%macro dopt 2
-               section .data16
-               db %1
-               dw dopt_%2
-               section .text16
-dopt_%2:
-%endmacro
-
-;
-; Parse individual DHCP options.  SI points to the option data and
-; AX to the option length.  DL contains the option number.
-; All registers are saved around the routine.
-;
-       dopt 1, subnet_mask
-               mov ebx,[si]
-               mov [Netmask],ebx
-               ret
-
-       dopt 3, router
-               mov ebx,[si]
-               mov [Gateway],ebx
-               ret
-
-       dopt 6, dns_servers
-               mov cx,ax
-               shr cx,2
-               cmp cl,DNS_MAX_SERVERS
-               jna .oklen
-               mov cl,DNS_MAX_SERVERS
-.oklen:
-               mov di,DNSServers
-               rep movsd
-               mov [LastDNSServer],di
-               ret
-
-       dopt 15, local_domain
-               mov bx,si
-               add bx,ax
-               xor ax,ax
-               xchg [bx],al    ; Zero-terminate option
-               mov di,LocalDomain
-               call dns_mangle ; Convert to DNS label set
-               mov [bx],al     ; Restore ending byte
-               ret
-
-       dopt 43, vendor_encaps
-               mov dh,208      ; Only recognize PXELINUX options
-               mov cx,ax       ; Length of option = max bytes to parse
-               call parse_some_dhcp_options    ; Parse recursive structure
-               ret
-
-       dopt 52, option_overload
-               mov bl,[si]
-               mov [OverLoad],bl
-               ret
-
-       dopt 54, server
-               mov eax,[si]
-               cmp dword [ServerIP],0
-               jne .skip               ; Already have a next server IP
-               call ip_ok
-               jz .skip
-               mov [ServerIP],eax
-.skip:         ret
-
-       dopt 61, client_identifier
-               cmp ax,MAC_MAX          ; Too long?
-               ja .skip
-               cmp ax,2                ; Too short?
-               jb .skip
-               cmp [MACLen],ah         ; Only do this if MACLen == 0
-               jne .skip
-               push ax
-               lodsb                   ; Client identifier type
-               cmp al,[MACType]
-               pop ax
-               jne .skip               ; Client identifier is not a MAC
-               dec ax
-               mov [MACLen],al
-               mov di,MAC
-               jmp dhcp_copyoption
-.skip:         ret
-
-       dopt 67, bootfile_name
-               mov di,BootFile
-               jmp dhcp_copyoption
-
-       dopt 97, uuid_client_identifier
-               cmp ax,17               ; type byte + 16 bytes UUID
-               jne .skip
-               mov dl,[si]             ; Must have type 0 == UUID
-               or dl,[HaveUUID]        ; Capture only the first instance
-               jnz .skip
-               mov byte [HaveUUID],1   ; Got UUID
-               mov di,UUIDType
-               jmp dhcp_copyoption
-.skip:         ret
-
-       dopt 209, pxelinux_configfile
-               mov di,ConfigName
-               or byte [DHCPMagic],2   ; Got config file
-               jmp dhcp_copyoption
-
-       dopt 210, pxelinux_pathprefix
-               mov di,PathPrefix
-               or byte [DHCPMagic],4   ; Got path prefix
-               jmp dhcp_copyoption
-
-       dopt 211, pxelinux_reboottime
-               cmp al,4
-               jne .done
-               mov ebx,[si]
-               xchg bl,bh              ; Convert to host byte order
-               rol ebx,16
-               xchg bl,bh
-               mov [RebootTime],ebx
-               or byte [DHCPMagic],8   ; Got RebootTime
-.done:         ret
-
-               ; Common code for copying an option verbatim
-               ; Copies the option into ES:DI and null-terminates it.
-               ; Returns with AX=0 and SI past the option.
-dhcp_copyoption:
-               xchg cx,ax      ; CX <- option length
-               rep movsb
-               xchg cx,ax      ; AX <- 0
-               stosb           ; Null-terminate
-               ret
-
-               section .data16
-dhcp_option_list_end:
-               section .text16
-
-               section .data16
-HaveUUID       db 0
-uuid_dashes    db 4,2,2,2,6,0  ; Bytes per UUID dashed section
-               section .text16
-
-;
-; genipopt
-;
-; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
-; option into IPOption based on a DHCP packet in trackbuf.
-; Assumes CS == DS == ES.
-;
-genipopt:
-               pushad
-               mov di,IPOption
-               mov eax,'ip='
-               stosd
-               dec di
-               mov eax,[MyIP]
-               call gendotquad
-               mov al,':'
-               stosb
-               mov eax,[ServerIP]
-               call gendotquad
-               mov al,':'
-               stosb
-               mov eax,[Gateway]
-               call gendotquad
-               mov al,':'
-               stosb
-               mov eax,[Netmask]
-               call gendotquad ; Zero-terminates its output
-               popad
-               ret
 
 ; -----------------------------------------------------------------------------
 ;  Common modules
@@ -2627,6 +720,8 @@ old_api_unload:
 ; PXE query packets partially filled in
 ;
                section .bss16
+                global pxe_bootp_query_pkt, pxe_udp_write_pkt
+                global pxe_udp_open_pkt, pxe_udp_read_pkt
 pxe_bootp_query_pkt:
 .status:       resw 1                  ; Status
 .packettype:   resw 1                  ; Boot server packet type
@@ -2662,7 +757,7 @@ pxe_udp_read_pkt:
 %if GPXE
 
                section .data16
-
+                global gpxe_file_api_check
 gpxe_file_api_check:
 .status:       dw 0                    ; Status
 .size:         dw 20                   ; Size in bytes
@@ -2672,6 +767,8 @@ gpxe_file_api_check:
 .flags:                dd 0
 
                section .bss16
+                global gpxe_file_read, gpxe_get_file_size
+                global gpxe_file_open
 
 gpxe_file_open:
 .status:       resw 1                  ; Status
@@ -2698,9 +795,9 @@ gpxe_file_read:
                section .data16
 
                alignz 4
+                global BaseStack
 BaseStack      dd StackTop             ; ESP of base stack
                dw 0                    ; SS of base stack
-NextSocket     dw 49152                ; Counter for allocating socket numbers
 KeepPXE                db 0                    ; Should PXE be kept around?
 
 ;
@@ -2727,18 +824,22 @@ tftp_opt_table:
 tftp_opts      equ ($-tftp_opt_table)/6
 
 ;
-; Error packet to return on options negotiation error
+; Error packet to return on TFTP protocol error
+; Most of our errors are OACK parsing errors, so use that error code
 ;
-tftp_opt_err   dw TFTP_ERROR                           ; ERROR packet
-               dw TFTP_EOPTNEG                         ; ERROR 8: bad options
-               db 'tsize option required', 0           ; Error message
-tftp_opt_err_len equ ($-tftp_opt_err)
+                global tftp_proto_err, tftp_proto_err_len
+tftp_proto_err dw TFTP_ERROR                           ; ERROR packet
+               dw TFTP_EOPTNEG                         ; ERROR 8: OACK error
+               db 'TFTP protocol error', 0             ; Error message
+tftp_proto_err_len equ ($-tftp_proto_err)
 
                alignz 4
+                global ack_packet_buf
 ack_packet_buf:        dw TFTP_ACK, 0                          ; TFTP ACK packet
 
 ;
 ; IP information (initialized to "unknown" values)
+                global MyIP, ServerIP, Netmask, Gateway, ServerPort
 MyIP           dd 0                    ; My IP address
 ServerIP       dd 0                    ; IP address of boot server
 Netmask                dd 0                    ; Netmask of this subnet