[syslinux:firmware] efi: Add network support

syslinux-bot for Matt Fleming matt.fleming at intel.com
Thu May 9 03:39:10 PDT 2013


Commit-ID:  fe283b78c973268f2d1f0309826ceeb5c9e8978d
Gitweb:     http://www.syslinux.org/commit/fe283b78c973268f2d1f0309826ceeb5c9e8978d
Author:     Matt Fleming <matt.fleming at intel.com>
AuthorDate: Fri, 22 Mar 2013 14:54:09 +0000
Committer:  Matt Fleming <matt.fleming at intel.com>
CommitDate: Tue, 23 Apr 2013 15:30:17 +0100

efi: Add network support

Add TCP and UDP support to the EFI firmware backend. This necessitated
moving all tcp functions to a core_tcp_* prefix so that they could be
implemented differently for BIOS+lwip and EFI. Unfortunately, the tcp_*
prefix is already in use by the lwip code.

To maintain symmetry, the UDP functions were also moved from net_core_*
to core_udp_*. The net_core API was introduced in 5.x to allow the
legacy PXE and lwip stacks to live side by side, and the intention was
that net_core_init() would take a protocol argument to build a
protocol-specific object. It turned out to be easier to call either udp
or tcp functions directly because the semantics of read/write differ
between protocols.

Booting an IPv4 EFI PXE stack using tftp and http has been tested.

There are a couple of TODO items left,

  o dns resolve code is missing
  o ftp hasn't been tested

Signed-off-by: Matt Fleming <matt.fleming at intel.com>

---
 core/Makefile         |   9 +-
 core/fs/pxe/bios.c    | 403 ++++++++++++++++++++++++++++++++++++++++++++++++++
 core/fs/pxe/core.c    | 124 +++++++++++++---
 core/fs/pxe/ftp.c     |  39 ++---
 core/fs/pxe/http.c    |  30 ++--
 core/fs/pxe/pxe.c     | 396 +------------------------------------------------
 core/fs/pxe/pxe.h     |  10 +-
 core/fs/pxe/tcp.c     |  59 +-------
 core/fs/pxe/tftp.c    |  26 ++--
 core/include/net.h    |  30 ++--
 core/legacynet/core.c |  18 +--
 efi/Makefile          |   4 +
 efi/efi.h             |  20 +++
 efi/main.c            |  91 +++++++++++-
 efi/pxe.c             | 136 +++++++++++++++++
 efi/tcp.c             | 232 +++++++++++++++++++++++++++++
 efi/udp.c             | 251 +++++++++++++++++++++++++++++++
 mk/efi.mk             |   2 +-
 mk/lib.mk             |   4 +-
 19 files changed, 1322 insertions(+), 562 deletions(-)

diff --git a/core/Makefile b/core/Makefile
index db295a7..87cf44b 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -53,7 +53,7 @@ SOBJ	 := $(subst $(SRC)/,,$(patsubst %.S,%.o,$(SSRC)))
 # To make this compatible with the following $(filter-out), make sure
 # we prefix everything with $(SRC)
 CORE_PXE_CSRC = \
-	$(addprefix $(SRC)/fs/pxe/, dhcp_option.c pxe.c tftp.c urlparse.c)
+	$(addprefix $(SRC)/fs/pxe/, dhcp_option.c pxe.c tftp.c urlparse.c bios.c)
 
 LPXELINUX_CSRC = $(CORE_PXE_CSRC) \
 	$(shell find $(SRC)/lwip -name '*.c' -print) \
@@ -82,6 +82,11 @@ endif
 COBJS	 = $(filter-out $(FILTER_OBJS),$(COBJ))
 SOBJS	 = $(filter-out $(FILTER_OBJS),$(SOBJ))
 
+ifdef EFI_BUILD
+COBJS += $(subst $(SRC)/,,$(CORE_PXE_CSRC:%.c=%.o) fs/pxe/ftp.o fs/pxe/ftp_readdir.o \
+	fs/pxe/http.o fs/pxe/http_readdir.o)
+endif
+
 LIB	 = libcom32.a
 LIBS	 = $(LIB) --whole-archive $(objdir)/com32/lib/libcom32core.a
 LIBDEP   = $(filter-out -% %start%,$(LIBS))
@@ -92,7 +97,7 @@ NASMOPT  += $(NASMDEBUG)
 
 PREPCORE = $(OBJ)/../lzo/prepcore
 
-CFLAGS += -D__SYSLINUX_CORE__
+CFLAGS += -D__SYSLINUX_CORE__ -I$(objdir)
 
 # The DATE is set on the make command line when building binaries for
 # official release.  Otherwise, substitute a hex string that is pretty much
diff --git a/core/fs/pxe/bios.c b/core/fs/pxe/bios.c
new file mode 100644
index 0000000..81aa715
--- /dev/null
+++ b/core/fs/pxe/bios.c
@@ -0,0 +1,403 @@
+#include <syslinux/firmware.h>
+#include <core.h>
+#include "pxe.h"
+#include <net.h>
+#include <minmax.h>
+#include <bios.h>
+
+static uint16_t real_base_mem;	   /* Amount of DOS memory after freeing */
+
+static bool has_gpxe;
+static uint32_t gpxe_funcs;
+
+/*
+ * Validity check on possible !PXE structure in buf
+ * return 1 for success, 0 for failure.
+ *
+ */
+static int is_pxe(const void *buf)
+{
+    const struct pxe_t *pxe = buf;
+    const uint8_t *p = buf;
+    int i = pxe->structlength;
+    uint8_t sum = 0;
+
+    if (i < sizeof(struct pxe_t) ||
+	memcmp(pxe->signature, "!PXE", 4))
+        return 0;
+
+    while (i--)
+        sum += *p++;
+
+    return sum == 0;
+}
+
+/*
+ * Just like is_pxe, it checks PXENV+ structure
+ *
+ */
+static int is_pxenv(const void *buf)
+{
+    const struct pxenv_t *pxenv = buf;
+    const uint8_t *p = buf;
+    int i = pxenv->length;
+    uint8_t sum = 0;
+
+    /* The pxeptr field isn't present in old versions */
+    if (i < offsetof(struct pxenv_t, pxeptr) ||
+	memcmp(pxenv->signature, "PXENV+", 6))
+        return 0;
+
+    while (i--)
+        sum += *p++;
+
+    return sum == 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.
+ *
+ *	return the corresponding pxe structure if found, or NULL;
+ */
+static const void *memory_scan(uintptr_t start, int (*func)(const void *))
+{
+    const char *ptr;
+
+    /* Scan each 16 bytes of conventional memory before the VGA region */
+    for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
+        if (func(ptr))
+            return ptr;		/* found it! */
+	ptr += 16;
+    }
+    return NULL;
+}
+
+static const struct pxe_t *memory_scan_for_pxe_struct(void)
+{
+    uint16_t start = bios_fbm(); /* Starting segment */
+
+    return memory_scan(start << 10, is_pxe);
+}
+
+static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
+{
+    return memory_scan(0x10000, 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
+ *
+ */
+int pxe_init(bool quiet)
+{
+    extern void pxe_int1a(void);
+    char plan = 'A';
+    uint16_t seg, off;
+    uint16_t code_seg, code_len;
+    uint16_t data_seg, data_len;
+    const char *base = GET_PTR(InitStack);
+    com32sys_t regs;
+    const char *type;
+    const struct pxenv_t *pxenv;
+    const struct pxe_t *pxe;
+
+    /* Assume API version 2.1 */
+    APIVer = 0x201;
+
+    /* Plan A: !PXE structure as SS:[SP + 4] */
+    off = *(const uint16_t *)(base + 48);
+    seg = *(const uint16_t *)(base + 50);
+    pxe = MK_PTR(seg, off);
+    if (is_pxe(pxe))
+        goto have_pxe;
+
+    /* Plan B: PXENV+ structure at [ES:BX] */
+    plan++;
+    off = *(const uint16_t *)(base + 24);  /* Original BX */
+    seg = *(const uint16_t *)(base + 4);   /* Original ES */
+    pxenv = MK_PTR(seg, off);
+    if (is_pxenv(pxenv))
+        goto have_pxenv;
+
+    /* Plan C: PXENV+ structure via INT 1Ah AX=5650h  */
+    plan++;
+    memset(&regs, 0, sizeof regs);
+    regs.eax.w[0] = 0x5650;
+    call16(pxe_int1a, &regs, &regs);
+    if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
+	pxenv = MK_PTR(regs.es, regs.ebx.w[0]);
+        if (is_pxenv(pxenv))
+            goto have_pxenv;
+    }
+
+    /* Plan D: !PXE memory scan */
+    plan++;
+    if ((pxe = memory_scan_for_pxe_struct()))
+        goto have_pxe;
+
+    /* Plan E: PXENV+ memory scan */
+    plan++;
+    if ((pxenv = memory_scan_for_pxenv_struct()))
+        goto have_pxenv;
+
+    /* Found nothing at all !! */
+    if (!quiet)
+	printf("No !PXE or PXENV+ API found; we're dead...\n");
+    return -1;
+
+ have_pxenv:
+    APIVer = pxenv->version;
+    if (!quiet)
+	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 (pxenv->length >= sizeof(struct pxenv_t)) {
+	    pxe = GET_PTR(pxenv->pxeptr);
+	    if (is_pxe(pxe))
+		goto 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
+	     */
+	    if ((pxe = memory_scan_for_pxe_struct()))
+		goto have_pxe;
+	}
+	APIVer = 0x200;		/* PXENV+ only, assume version 2.00 */
+    }
+
+    /* Otherwise, no dice, use PXENV+ structure */
+    data_len = pxenv->undidatasize;
+    data_seg = pxenv->undidataseg;
+    code_len = pxenv->undicodesize;
+    code_seg = pxenv->undicodeseg;
+    PXEEntry = pxenv->rmentry;
+    type = "PXENV+";
+    goto have_entrypoint;
+
+ have_pxe:
+    data_len = pxe->seg[PXE_Seg_UNDIData].size;
+    data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
+    code_len = pxe->seg[PXE_Seg_UNDICode].size;
+    code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
+    PXEEntry = pxe->entrypointsp;
+    type = "!PXE";
+
+ have_entrypoint:
+    if (!quiet) {
+	printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
+	       type, PXEEntry.seg, PXEEntry.offs, plan);
+	printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
+	printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
+    }
+
+    code_seg = code_seg + ((code_len + 15) >> 4);
+    data_seg = data_seg + ((data_len + 15) >> 4);
+
+    real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
+
+    probe_undi();
+
+    return 0;
+}
+
+/*
+ * See if we have gPXE
+ */
+void gpxe_init(void)
+{
+    int err;
+    static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
+
+    if (APIVer >= 0x201) {
+	api_check.Size = sizeof api_check;
+	api_check.Magic = 0x91d447b2;
+	err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
+	if (!err && api_check.Magic == 0xe9c17b20)
+	    gpxe_funcs = api_check.APIMask;
+    }
+
+    /* Necessary functions for us to use the gPXE file API */
+    has_gpxe = (~gpxe_funcs & 0x4b) == 0;
+}
+
+
+/**
+ * Get a DHCP packet from the PXE stack into a lowmem buffer
+ *
+ * @param:  type,  packet type
+ * @return: buffer size
+ *
+ */
+static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
+{
+    int err;
+    static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
+    printf(" %02x", type);
+
+    memset(&get_cached_info, 0, sizeof get_cached_info);
+    get_cached_info.PacketType  = type;
+    get_cached_info.BufferSize  = bufsiz;
+    get_cached_info.Buffer      = FAR_PTR(buf);
+    err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
+    if (err) {
+        printf("PXE API call failed, error  %04x\n", err);
+	kaboom();
+    }
+
+    return get_cached_info.BufferSize;
+}
+
+/*
+ * This function unloads the PXE and UNDI stacks and
+ * unclaims the memory.
+ */
+__export void unload_pxe(uint16_t flags)
+{
+    /* PXE unload sequences */
+    /*
+     * iPXE does:
+     * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
+     * Older Syslinux did:
+     * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
+     */
+    static const uint8_t new_api_unload[] = {
+	PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
+    };
+    static const uint8_t old_api_unload[] = {
+	PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
+    };
+
+    unsigned int api;
+    const uint8_t *api_ptr;
+    int err;
+    size_t int_addr;
+    static __lowmem union {
+	struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
+	struct s_PXENV_UNLOAD_STACK unload_stack;
+	struct s_PXENV_STOP_UNDI stop_undi;
+	struct s_PXENV_UNDI_CLEANUP undi_cleanup;
+	uint16_t Status;	/* All calls have this as the first member */
+    } unload_call;
+
+    dprintf("Called unload_pxe()...\n");
+    dprintf("FBM before unload = %d\n", bios_fbm());
+
+    err = reset_pxe();
+
+    dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err);
+
+    /* If we want to keep PXE around, we still need to reset it */
+    if (flags || err)
+	return;
+
+    dprintf("APIVer = %04x\n", APIVer);
+
+    api_ptr = APIVer >= 0x0200 ? new_api_unload : old_api_unload;
+    while((api = *api_ptr++)) {
+	dprintf("PXE call %04x\n", api);
+	memset(&unload_call, 0, sizeof unload_call);
+	err = pxe_call(api, &unload_call);
+	if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
+	    printf("PXE unload API call %04x failed: 0x%x\n",
+		   api, unload_call.Status);
+	    goto cant_free;
+	}
+    }
+
+    api = 0xff00;
+    if (real_base_mem <= bios_fbm()) {  /* Sanity check */
+	dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem);
+	goto cant_free;
+    }
+    api++;
+
+    /* Check that PXE actually unhooked the INT 0x1A chain */
+    int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
+    int_addr >>= 10;
+    if (int_addr >= real_base_mem || int_addr < bios_fbm()) {
+	set_bios_fbm(real_base_mem);
+	dprintf("FBM after unload_pxe = %d\n", bios_fbm());
+	return;
+    }
+
+    dprintf("Can't free FBM, real_base_mem = %d, "
+	    "FBM = %d, INT 1A = %08x (%d)\n",
+	    real_base_mem, bios_fbm(),
+	    *(uint32_t *)(4 * 0x1a), int_addr);
+
+cant_free:
+    printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
+	   api, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem);
+    return;
+}
+
+void net_parse_dhcp(void)
+{
+    int pkt_len;
+    struct bootp_t *bp;
+    const size_t dhcp_max_packet = 4096;
+
+    bp = lmalloc(dhcp_max_packet);
+    if (!bp) {
+	printf("Out of low memory\n");
+	kaboom();
+    }
+
+    *LocalDomain = 0;   /* No LocalDomain received */
+
+    /*
+     * Get the DHCP client identifiers (query info 1)
+     */
+    printf("Getting cached packet ");
+    pkt_len = pxe_get_cached_info(1, bp, dhcp_max_packet);
+    parse_dhcp(bp, 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, bp, dhcp_max_packet);
+    parse_dhcp(bp, 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
+     */
+    MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
+    MAC_type = bp->hardware;
+    memcpy(MAC, bp->macaddr, MAC_len);
+
+    /*
+     * Get the boot file and other info. This lives in the CACHED_REPLY
+     * packet (query info 3)
+     */
+    pkt_len = pxe_get_cached_info(3, bp, dhcp_max_packet);
+    parse_dhcp(bp, pkt_len);
+    printf("\n");
+
+    lfree(bp);
+}
diff --git a/core/fs/pxe/core.c b/core/fs/pxe/core.c
index e330ba8..e6bbee9 100644
--- a/core/fs/pxe/core.c
+++ b/core/fs/pxe/core.c
@@ -17,29 +17,15 @@ const struct url_scheme url_schemes[] = {
  * Open a socket
  *
  * @param:socket, the socket to open
- * @param:proto, the protocol of the new connection
  *
  * @out: error code, 0 on success, -1 on failure
  */
-int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto)
+int core_udp_open(struct pxe_pvt_inode *socket)
 {
     struct net_private_lwip *priv = &socket->net.lwip;
-    enum netconn_type type;
     int err;
 
-    switch (proto) {
-    case NET_CORE_TCP:
-	type = NETCONN_TCP;
-	break;
-    case NET_CORE_UDP:
-	type = NETCONN_UDP;
-	break;
-    default:
-	type = NETCONN_INVALID;
-	break;
-    }
-
-    priv->conn = netconn_new(type);
+    priv->conn = netconn_new(NETCONN_UDP);
     if (!priv->conn)
 	return -1;
 
@@ -58,7 +44,7 @@ int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto)
  *
  * @param:socket, the socket to open
  */
-void net_core_close(struct pxe_pvt_inode *socket)
+void core_udp_close(struct pxe_pvt_inode *socket)
 {
     struct net_private_lwip *priv = &socket->net.lwip;
 
@@ -75,7 +61,7 @@ void net_core_close(struct pxe_pvt_inode *socket)
  * @param:ip, the ip address
  * @param:port, the port number, host-byte order
  */
-void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
+void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
 		      uint16_t port)
 {
     struct net_private_lwip *priv = &socket->net.lwip;
@@ -90,7 +76,7 @@ void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
  *
  * @param:socket, the open socket
  */
-void net_core_disconnect(struct pxe_pvt_inode *socket)
+void core_udp_disconnect(struct pxe_pvt_inode *socket)
 {
     struct net_private_lwip *priv = &socket->net.lwip;
     netconn_disconnect(priv->conn);
@@ -106,7 +92,7 @@ void net_core_disconnect(struct pxe_pvt_inode *socket)
  * @out: src_ip, ip address of the data source
  * @out: src_port, port number of the data source, host-byte order
  */
-int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
 		  uint32_t *src_ip, uint16_t *src_port)
 {
     struct net_private_lwip *priv = &socket->net.lwip;
@@ -143,7 +129,7 @@ int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
  * @param:data, data buffer to send
  * @param:len, size of data bufer
  */
-void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
+void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
 {
     struct netconn *conn = socket->net.lwip.conn;
     struct netbuf *nbuf;
@@ -212,3 +198,99 @@ void probe_undi(void)
 	   pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags);
 }
 
+int core_tcp_open(struct pxe_pvt_inode *socket)
+{
+    socket->net.lwip.conn = netconn_new(NETCONN_TCP);
+    if (!socket->net.lwip.conn)
+	return -1;
+
+    return 0;
+}
+int core_tcp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port)
+{
+    struct ip_addr addr;
+    err_t err;
+
+    addr.addr = ip;
+    err = netconn_connect(socket->net.lwip.conn, &addr, port);
+    if (err) {
+	printf("netconn_connect error %d\n", err);
+	return -1;
+    }
+
+    return 0;
+}
+
+int core_tcp_write(struct pxe_pvt_inode *socket, const void *data, size_t len,
+		   bool copy)
+{
+    err_t err;
+    u8_t flags = copy ? NETCONN_COPY : NETCONN_NOCOPY;
+
+    err = netconn_write(socket->net.lwip.conn, data, len, flags);
+    if (err) {
+	printf("netconn_write failed: %d\n", err);
+	return -1;
+    }
+
+    return 0;
+}
+
+void core_tcp_close_file(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+
+    if (socket->net.lwip.conn) {
+	netconn_delete(socket->net.lwip.conn);
+	socket->net.lwip.conn = NULL;
+    }
+    if (socket->net.lwip.buf) {
+	netbuf_delete(socket->net.lwip.buf);
+        socket->net.lwip.buf = NULL;
+    }
+}
+
+bool core_tcp_is_connected(struct pxe_pvt_inode *socket)
+{
+    if (socket->net.lwip.conn)
+	return true;
+
+    return false;
+}
+
+void core_tcp_fill_buffer(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    void *data;
+    u16_t len;
+    err_t err;
+
+    /* Clean up or advance an inuse netbuf */
+    if (socket->net.lwip.buf) {
+	if (netbuf_next(socket->net.lwip.buf) < 0) {
+	    netbuf_delete(socket->net.lwip.buf);
+	    socket->net.lwip.buf = NULL;
+	}
+    }
+    /* If needed get a new netbuf */
+    if (!socket->net.lwip.buf) {
+	err = netconn_recv(socket->net.lwip.conn, &(socket->net.lwip.buf));
+	if (!socket->net.lwip.buf || err) {
+	    socket->tftp_goteof = 1;
+	    if (inode->size == -1)
+		inode->size = socket->tftp_filepos;
+	    socket->ops->close(inode);
+	    return;
+	}
+    }
+    /* Report the current fragment of the netbuf */
+    err = netbuf_data(socket->net.lwip.buf, &data, &len);
+    if (err) {
+	printf("netbuf_data err: %d\n", err);
+	kaboom();
+    }
+    socket->tftp_dataptr = data;
+    socket->tftp_filepos += len;
+    socket->tftp_bytesleft = len;
+    return;
+}
diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c
index c2d155a..06c232e 100644
--- a/core/fs/pxe/ftp.c
+++ b/core/fs/pxe/ftp.c
@@ -26,6 +26,7 @@
 #include "pxe.h"
 #include "thread.h"
 #include "url.h"
+#include "net.h"
 
 static int ftp_cmd_response(struct inode *inode, const char *cmd,
 			    const char *cmd_arg,
@@ -37,11 +38,11 @@ static int ftp_cmd_response(struct inode *inode, const char *cmd,
     int pb, pn;
     bool ps;
     bool first_line, done;
-    err_t err;
     char cmd_buf[4096];
     int cmd_len;
     const char *p;
     char *q;
+    int err;
 
     if (cmd) {
 	cmd_len = strlcpy(cmd_buf, cmd, sizeof cmd_buf);
@@ -69,7 +70,7 @@ static int ftp_cmd_response(struct inode *inode, const char *cmd,
 	*q++ = '\n';
 	cmd_len += 2;
 
-	err = netconn_write(socket->net.lwip.conn, cmd_buf, cmd_len, NETCONN_COPY);
+	err = core_tcp_write(socket, cmd_buf, cmd_len, true);
 	if (err)
 	    return -1;
     }
@@ -152,11 +153,11 @@ static void ftp_free(struct inode *inode)
     struct pxe_pvt_inode *socket = PVT(inode);
 
     if (socket->ctl) {
-	tcp_close_file(socket->ctl);
+	core_tcp_close_file(socket->ctl);
 	free_socket(socket->ctl);
 	socket->ctl = NULL;
     }
-    tcp_close_file(inode);
+    core_tcp_close_file(inode);
 }
 
 static void ftp_close_file(struct inode *inode)
@@ -166,7 +167,7 @@ static void ftp_close_file(struct inode *inode)
     int resp;
 
     ctlsock = socket->ctl ? PVT(socket->ctl) : NULL;
-    if (ctlsock->net.lwip.conn) {
+    if (core_tcp_is_connected(ctlsock)) {
 	resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL);
 	while (resp == 226) {
 	    resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
@@ -176,7 +177,7 @@ static void ftp_close_file(struct inode *inode)
 }
 
 static const struct pxe_conn_ops ftp_conn_ops = {
-    .fill_buffer	= tcp_fill_buffer,
+    .fill_buffer	= core_tcp_fill_buffer,
     .close		= ftp_close_file,
     .readdir		= ftp_readdir,
 };
@@ -186,7 +187,6 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
 {
     struct pxe_pvt_inode *socket = PVT(inode);
     struct pxe_pvt_inode *ctlsock;
-    struct ip_addr addr;
     uint8_t pasv_data[6];
     int pasv_bytes;
     int resp;
@@ -209,11 +209,9 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
 	return;
     ctlsock = PVT(socket->ctl);
     ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */
-    ctlsock->net.lwip.conn = netconn_new(NETCONN_TCP);
-    if (!ctlsock->net.lwip.conn)
+    if (core_tcp_open(ctlsock))
 	goto err_free;
-    addr.addr = url->ip;
-    err = netconn_connect(ctlsock->net.lwip.conn, &addr, url->port);
+    err = core_tcp_connect(ctlsock, url->ip, url->port);
     if (err)
 	goto err_delete;
 
@@ -248,11 +246,11 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
     if (resp != 227 || pasv_bytes != 6)
 	goto err_disconnect;
 
-    socket->net.lwip.conn = netconn_new(NETCONN_TCP);
-    if (!socket->net.lwip.conn)
+    err = core_tcp_open(socket);
+    if (err)
 	goto err_disconnect;
-    err = netconn_connect(socket->net.lwip.conn, (struct ip_addr *)&pasv_data[0],
-			  ntohs(*(uint16_t *)&pasv_data[4]));
+    err = core_tcp_connect(socket, ntohl(*(uint32_t*)&pasv_data[0]),
+			   ntohs(*(uint16_t *)&pasv_data[4]));
     if (err)
 	goto err_disconnect;
 
@@ -266,15 +264,10 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
     return;			/* Sucess! */
 
 err_disconnect:
-    if (ctlsock->net.lwip.conn)
-	netconn_write(ctlsock->net.lwip.conn, "QUIT\r\n", 6, NETCONN_NOCOPY);
-    if (socket->net.lwip.conn)
-	netconn_delete(socket->net.lwip.conn);
-    if (ctlsock->net.lwip.buf)
-	netbuf_delete(ctlsock->net.lwip.buf);
+    core_tcp_write(ctlsock, "QUIT\r\n", 6, false);
+    core_tcp_close_file(inode);
 err_delete:
-    if (ctlsock->net.lwip.conn)
-	netconn_delete(ctlsock->net.lwip.conn);
+    core_tcp_close_file(socket->ctl);
 err_free:
     free_socket(socket->ctl);
 }
diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c
index 950c8bb..5722372 100644
--- a/core/fs/pxe/http.c
+++ b/core/fs/pxe/http.c
@@ -4,6 +4,7 @@
 #include "pxe.h"
 #include "version.h"
 #include "url.h"
+#include "net.h"
 
 #define HTTP_PORT	80
 
@@ -146,8 +147,8 @@ void http_bake_cookies(void)
 }
 
 static const struct pxe_conn_ops http_conn_ops = {
-    .fill_buffer	= tcp_fill_buffer,
-    .close		= tcp_close_file,
+    .fill_buffer	= core_tcp_fill_buffer,
+    .close		= core_tcp_close_file,
     .readdir		= http_readdir,
 };
 
@@ -160,7 +161,6 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
     char field_name[20];
     char field_value[1024];
     size_t field_name_len, field_value_len;
-    err_t err;
     enum state {
 	st_httpver,
 	st_stcode,
@@ -172,12 +172,12 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
 	st_skip_fieldvalue,
 	st_eoh,
     } state;
-    struct ip_addr addr;
     static char location[FILENAME_MAX];
     uint32_t content_length; /* same as inode->size */
     size_t response_size;
     int status;
     int pos;
+    int err;
 
     (void)flags;
 
@@ -191,21 +191,16 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
     inode->size = content_length = -1;
 
     /* Start the http connection */
-    socket->net.lwip.conn = netconn_new(NETCONN_TCP);
-    if (!socket->net.lwip.conn) {
-	printf("netconn_new failed\n");
+    err = core_tcp_open(socket);
+    if (err)
         return;
-    }
 
-    addr.addr = url->ip;
     if (!url->port)
 	url->port = HTTP_PORT;
 
-    err = netconn_connect(socket->net.lwip.conn, &addr, url->port);
-    if (err) {
-	printf("netconn_connect error %d\n", err);
+    err = core_tcp_connect(socket, url->ip, url->port);
+    if (err)
 	goto fail;
-    }
 
     strcpy(header_buf, "GET /");
     header_bytes = 5;
@@ -225,12 +220,9 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
     if (header_bytes >= header_len)
 	goto fail;		/* Buffer overflow */
 
-    err = netconn_write(socket->net.lwip.conn, header_buf,
-			header_bytes, NETCONN_NOCOPY);
-    if (err) {
-	printf("netconn_write error %d\n", err);
+    err = core_tcp_write(socket, header_buf, header_bytes, false);
+    if (err)
 	goto fail;
-    }
 
     /* Parse the HTTP header */
     state = st_httpver;
@@ -395,6 +387,6 @@ void http_open(struct url_info *url, int flags, struct inode *inode,
     return;
 fail:
     inode->size = 0;
-    tcp_close_file(inode);
+    core_tcp_close_file(inode);
     return;
 }
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index 3f68e96..4591edc 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -2,9 +2,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <core.h>
-#include <bios.h>
 #include <fs.h>
-#include <minmax.h>
 #include <fcntl.h>
 #include <sys/cpu.h>
 #include "pxe.h"
@@ -16,8 +14,6 @@
 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
 __lowmem t_PXENV_UNDI_GET_IFACE_INFO  pxe_undi_iface;
 
-static uint16_t real_base_mem;	   /* Amount of DOS memory after freeing */
-
 uint8_t MAC[MAC_MAX];		   /* Actual MAC address */
 uint8_t MAC_len;                   /* MAC address len */
 uint8_t MAC_type;                  /* MAC address type */
@@ -25,8 +21,6 @@ uint8_t MAC_type;                  /* MAC address type */
 char boot_file[256];		   /* From DHCP */
 char path_prefix[256];		   /* From DHCP */
 
-static bool has_gpxe;
-static uint32_t gpxe_funcs;
 bool have_uuid = false;
 
 /*
@@ -131,32 +125,6 @@ __export int pxe_call(int opcode, void *data)
     return regs.eflags.l & EFLAGS_CF;  /* CF SET if fail */
 }
 
-/**
- * Get a DHCP packet from the PXE stack into a lowmem buffer
- *
- * @param:  type,  packet type
- * @return: buffer size
- *
- */
-static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
-{
-    int err;
-    static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
-    printf(" %02x", type);
-
-    memset(&get_cached_info, 0, sizeof get_cached_info);
-    get_cached_info.PacketType  = type;
-    get_cached_info.BufferSize  = bufsiz;
-    get_cached_info.Buffer      = FAR_PTR(buf);
-    err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
-    if (err) {
-        printf("PXE API call failed, error  %04x\n", err);
-	kaboom();
-    }
-
-    return get_cached_info.BufferSize;
-}
-
 /*
  * mangle a filename pointed to by _src_ into a buffer pointed
  * to by _dst_; ends on encountering any whitespace.
@@ -568,289 +536,11 @@ static void ip_init(void)
 }
 
 /*
- * Validity check on possible !PXE structure in buf
- * return 1 for success, 0 for failure.
- *
- */
-static int is_pxe(const void *buf)
-{
-    const struct pxe_t *pxe = buf;
-    const uint8_t *p = buf;
-    int i = pxe->structlength;
-    uint8_t sum = 0;
-
-    if (i < sizeof(struct pxe_t) ||
-	memcmp(pxe->signature, "!PXE", 4))
-        return 0;
-
-    while (i--)
-        sum += *p++;
-
-    return sum == 0;
-}
-
-/*
- * Just like is_pxe, it checks PXENV+ structure
- *
- */
-static int is_pxenv(const void *buf)
-{
-    const struct pxenv_t *pxenv = buf;
-    const uint8_t *p = buf;
-    int i = pxenv->length;
-    uint8_t sum = 0;
-
-    /* The pxeptr field isn't present in old versions */
-    if (i < offsetof(struct pxenv_t, pxeptr) ||
-	memcmp(pxenv->signature, "PXENV+", 6))
-        return 0;
-
-    while (i--)
-        sum += *p++;
-
-    return sum == 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.
- *
- *	return the corresponding pxe structure if found, or NULL;
- */
-static const void *memory_scan(uintptr_t start, int (*func)(const void *))
-{
-    const char *ptr;
-
-    /* Scan each 16 bytes of conventional memory before the VGA region */
-    for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
-        if (func(ptr))
-            return ptr;		/* found it! */
-	ptr += 16;
-    }
-    return NULL;
-}
-
-static const struct pxe_t *memory_scan_for_pxe_struct(void)
-{
-    uint16_t start = bios_fbm(); /* Starting segment */
-
-    return memory_scan(start << 10, is_pxe);
-}
-
-static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
-{
-    return memory_scan(0x10000, 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
- *
- */
-static int pxe_init(bool quiet)
-{
-    extern void pxe_int1a(void);
-    char plan = 'A';
-    uint16_t seg, off;
-    uint16_t code_seg, code_len;
-    uint16_t data_seg, data_len;
-    const char *base = GET_PTR(InitStack);
-    com32sys_t regs;
-    const char *type;
-    const struct pxenv_t *pxenv;
-    const struct pxe_t *pxe;
-
-    /* Assume API version 2.1 */
-    APIVer = 0x201;
-
-    /* Plan A: !PXE structure as SS:[SP + 4] */
-    off = *(const uint16_t *)(base + 48);
-    seg = *(const uint16_t *)(base + 50);
-    pxe = MK_PTR(seg, off);
-    if (is_pxe(pxe))
-        goto have_pxe;
-
-    /* Plan B: PXENV+ structure at [ES:BX] */
-    plan++;
-    off = *(const uint16_t *)(base + 24);  /* Original BX */
-    seg = *(const uint16_t *)(base + 4);   /* Original ES */
-    pxenv = MK_PTR(seg, off);
-    if (is_pxenv(pxenv))
-        goto have_pxenv;
-
-    /* Plan C: PXENV+ structure via INT 1Ah AX=5650h  */
-    plan++;
-    memset(&regs, 0, sizeof regs);
-    regs.eax.w[0] = 0x5650;
-    call16(pxe_int1a, &regs, &regs);
-    if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
-	pxenv = MK_PTR(regs.es, regs.ebx.w[0]);
-        if (is_pxenv(pxenv))
-            goto have_pxenv;
-    }
-
-    /* Plan D: !PXE memory scan */
-    plan++;
-    if ((pxe = memory_scan_for_pxe_struct()))
-        goto have_pxe;
-
-    /* Plan E: PXENV+ memory scan */
-    plan++;
-    if ((pxenv = memory_scan_for_pxenv_struct()))
-        goto have_pxenv;
-
-    /* Found nothing at all !! */
-    if (!quiet)
-	printf("No !PXE or PXENV+ API found; we're dead...\n");
-    return -1;
-
- have_pxenv:
-    APIVer = pxenv->version;
-    if (!quiet)
-	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 (pxenv->length >= sizeof(struct pxenv_t)) {
-	    pxe = GET_PTR(pxenv->pxeptr);
-	    if (is_pxe(pxe))
-		goto 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
-	     */
-	    if ((pxe = memory_scan_for_pxe_struct()))
-		goto have_pxe;
-	}
-	APIVer = 0x200;		/* PXENV+ only, assume version 2.00 */
-    }
-
-    /* Otherwise, no dice, use PXENV+ structure */
-    data_len = pxenv->undidatasize;
-    data_seg = pxenv->undidataseg;
-    code_len = pxenv->undicodesize;
-    code_seg = pxenv->undicodeseg;
-    PXEEntry = pxenv->rmentry;
-    type = "PXENV+";
-    goto have_entrypoint;
-
- have_pxe:
-    data_len = pxe->seg[PXE_Seg_UNDIData].size;
-    data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
-    code_len = pxe->seg[PXE_Seg_UNDICode].size;
-    code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
-    PXEEntry = pxe->entrypointsp;
-    type = "!PXE";
-
- have_entrypoint:
-    if (!quiet) {
-	printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
-	       type, PXEEntry.seg, PXEEntry.offs, plan);
-	printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
-	printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
-    }
-
-    code_seg = code_seg + ((code_len + 15) >> 4);
-    data_seg = data_seg + ((data_len + 15) >> 4);
-
-    real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
-
-    probe_undi();
-
-    return 0;
-}
-
-/*
- * See if we have gPXE
- */
-static void gpxe_init(void)
-{
-    int err;
-    static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
-
-    if (APIVer >= 0x201) {
-	api_check.Size = sizeof api_check;
-	api_check.Magic = 0x91d447b2;
-	err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
-	if (!err && api_check.Magic == 0xe9c17b20)
-	    gpxe_funcs = api_check.APIMask;
-    }
-
-    /* Necessary functions for us to use the gPXE file API */
-    has_gpxe = (~gpxe_funcs & 0x4b) == 0;
-}
-
-/*
  * Network-specific initialization
  */
 static void network_init(void)
 {
-    int pkt_len;
-    struct bootp_t *bp;
-    const size_t dhcp_max_packet = 4096;
-
-    bp = lmalloc(dhcp_max_packet);
-    if (!bp) {
-	printf("Out of low memory\n");
-	kaboom();
-    }
-
-    *LocalDomain = 0;   /* No LocalDomain received */
-
-    /*
-     * Get the DHCP client identifiers (query info 1)
-     */
-    printf("Getting cached packet ");
-    pkt_len = pxe_get_cached_info(1, bp, dhcp_max_packet);
-    parse_dhcp(bp, 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, bp, dhcp_max_packet);
-    parse_dhcp(bp, 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
-     */
-    MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
-    MAC_type = bp->hardware;
-    memcpy(MAC, bp->macaddr, MAC_len);
-
-    /*
-     * Get the boot file and other info. This lives in the CACHED_REPLY
-     * packet (query info 3)
-     */
-    pkt_len = pxe_get_cached_info(3, bp, dhcp_max_packet);
-    parse_dhcp(bp, pkt_len);
-    printf("\n");
-
-    lfree(bp);
+    net_parse_dhcp();
 
     make_bootif_string();
     /* If DMI and DHCP disagree, which one should we set? */
@@ -981,90 +671,6 @@ static void install_int18_hack(void)
 }
 #endif
 
-/*
- * This function unloads the PXE and UNDI stacks and
- * unclaims the memory.
- */
-__export void unload_pxe(uint16_t flags)
-{
-    /* PXE unload sequences */
-    /*
-     * iPXE does:
-     * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
-     * Older Syslinux did:
-     * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
-     */
-    static const uint8_t new_api_unload[] = {
-	PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
-    };
-    static const uint8_t old_api_unload[] = {
-	PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
-    };
-
-    unsigned int api;
-    const uint8_t *api_ptr;
-    int err;
-    size_t int_addr;
-    static __lowmem union {
-	struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
-	struct s_PXENV_UNLOAD_STACK unload_stack;
-	struct s_PXENV_STOP_UNDI stop_undi;
-	struct s_PXENV_UNDI_CLEANUP undi_cleanup;
-	uint16_t Status;	/* All calls have this as the first member */
-    } unload_call;
-
-    dprintf("Called unload_pxe()...\n");
-    dprintf("FBM before unload = %d\n", bios_fbm());
-
-    err = reset_pxe();
-
-    dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err);
-
-    /* If we want to keep PXE around, we still need to reset it */
-    if (flags || err)
-	return;
-
-    dprintf("APIVer = %04x\n", APIVer);
-
-    api_ptr = APIVer >= 0x0200 ? new_api_unload : old_api_unload;
-    while((api = *api_ptr++)) {
-	dprintf("PXE call %04x\n", api);
-	memset(&unload_call, 0, sizeof unload_call);
-	err = pxe_call(api, &unload_call);
-	if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
-	    printf("PXE unload API call %04x failed: 0x%x\n",
-		   api, unload_call.Status);
-	    goto cant_free;
-	}
-    }
-
-    api = 0xff00;
-    if (real_base_mem <= bios_fbm()) {  /* Sanity check */
-	dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem);
-	goto cant_free;
-    }
-    api++;
-
-    /* Check that PXE actually unhooked the INT 0x1A chain */
-    int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
-    int_addr >>= 10;
-    if (int_addr >= real_base_mem || int_addr < bios_fbm()) {
-	set_bios_fbm(real_base_mem);
-	dprintf("FBM after unload_pxe = %d\n", bios_fbm());
-	return;
-    }
-
-    dprintf("Can't free FBM, real_base_mem = %d, "
-	    "FBM = %d, INT 1A = %08x (%d)\n",
-	    real_base_mem, bios_fbm(),
-	    *(uint32_t *)(4 * 0x1a), int_addr);
-
-cant_free:
-    printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
-	   api, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem);
-    return;
-}
-
 static int pxe_readdir(struct file *file, struct dirent *dirent)
 {
     struct inode *inode = file->inode;
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index 3f511fd..f4b9b6e 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -107,6 +107,8 @@ struct bootp_t {
 
 struct netconn;
 struct netbuf;
+struct efi_binding;
+
 /*
  * Our inode private information -- this includes the packet buffer!
  */
@@ -125,6 +127,9 @@ union net_private {
 	uint32_t remoteip;  	  /* Remote IP address (0 = disconnected) */
 	uint16_t localport;   	  /* Local port number  (0=not in use) */
     } tftp;
+    struct net_private_efi {
+	struct efi_binding *binding; /* EFI binding for protocol */
+    } efi;
 };
 
 struct pxe_pvt_inode {
@@ -254,8 +259,9 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode,
 int ftp_readdir(struct inode *inode, struct dirent *dirent);
 
 /* tcp.c */
-void tcp_close_file(struct inode *inode);
-void tcp_fill_buffer(struct inode *inode);
 const struct pxe_conn_ops tcp_conn_ops;
 
+extern void gpxe_init(void);
+extern int pxe_init(bool quiet);
+
 #endif /* pxe.h */
diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c
index 576aa29..0fb6efd 100644
--- a/core/fs/pxe/tcp.c
+++ b/core/fs/pxe/tcp.c
@@ -16,63 +16,10 @@
  * Common operations for TCP-based network protocols
  */
 
-#include <lwip/api.h>
 #include "pxe.h"
-#include "version.h"
-#include "url.h"
-
-void tcp_close_file(struct inode *inode)
-{
-    struct pxe_pvt_inode *socket = PVT(inode);
-
-    if (socket->net.lwip.conn) {
-	netconn_delete(socket->net.lwip.conn);
-	socket->net.lwip.conn = NULL;
-    }
-    if (socket->net.lwip.buf) {
-	netbuf_delete(socket->net.lwip.buf);
-        socket->net.lwip.buf = NULL;
-    }
-}
-
-void tcp_fill_buffer(struct inode *inode)
-{
-    struct pxe_pvt_inode *socket = PVT(inode);
-    void *data;
-    u16_t len;
-    err_t err;
-
-    /* Clean up or advance an inuse netbuf */
-    if (socket->net.lwip.buf) {
-	if (netbuf_next(socket->net.lwip.buf) < 0) {
-	    netbuf_delete(socket->net.lwip.buf);
-	    socket->net.lwip.buf = NULL;
-	}
-    }
-    /* If needed get a new netbuf */
-    if (!socket->net.lwip.buf) {
-	err = netconn_recv(socket->net.lwip.conn, &(socket->net.lwip.buf));
-	if (!socket->net.lwip.buf || err) {
-	    socket->tftp_goteof = 1;
-	    if (inode->size == -1)
-		inode->size = socket->tftp_filepos;
-	    socket->ops->close(inode);
-	    return;
-	}
-    }
-    /* Report the current fragment of the netbuf */
-    err = netbuf_data(socket->net.lwip.buf, &data, &len);
-    if (err) {
-	printf("netbuf_data err: %d\n", err);
-	kaboom();
-    }
-    socket->tftp_dataptr = data;
-    socket->tftp_filepos += len;
-    socket->tftp_bytesleft = len;
-    return;
-}
+#include "net.h"
 
 const struct pxe_conn_ops tcp_conn_ops = {
-    .fill_buffer	= tcp_fill_buffer,
-    .close		= tcp_close_file,
+    .fill_buffer	= core_tcp_fill_buffer,
+    .close		= core_tcp_close_file,
 };
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
index 9b755c9..b5dc72e 100644
--- a/core/fs/pxe/tftp.c
+++ b/core/fs/pxe/tftp.c
@@ -38,7 +38,7 @@ static void tftp_close_file(struct inode *inode)
     if (!socket->tftp_goteof) {
 	tftp_error(inode, 0, "No error, file close");
     }
-    net_core_close(socket);
+    core_udp_close(socket);
 }
 
 /**
@@ -64,7 +64,7 @@ static void tftp_error(struct inode *inode, uint16_t errnum,
     memcpy(err_buf.err_msg, errstr, len);
     err_buf.err_msg[len] = '\0';
 
-    net_core_send(socket, &err_buf, 4 + len + 1);
+    core_udp_send(socket, &err_buf, 4 + len + 1);
 }
 
 /**
@@ -83,7 +83,7 @@ static void ack_packet(struct inode *inode, uint16_t ack_num)
     ack_packet_buf[0]     = TFTP_ACK;
     ack_packet_buf[1]     = htons(ack_num);
 
-    net_core_send(socket, ack_packet_buf, 4);
+    core_udp_send(socket, ack_packet_buf, 4);
 }
 
 /*
@@ -118,7 +118,7 @@ static void tftp_get_packet(struct inode *inode)
 
     while (timeout) {
 	buf_len = socket->tftp_blksize + 4;
-	err = net_core_recv(socket, socket->tftp_pktbuf, &buf_len,
+	err = core_udp_recv(socket, socket->tftp_pktbuf, &buf_len,
 			    &src_ip, &src_port);
 	if (err) {
 	    jiffies_t now = jiffies();
@@ -238,7 +238,7 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode,
 	url->port = TFTP_PORT;
 
     socket->ops = &tftp_conn_ops;
-    if (net_core_open(socket, NET_CORE_UDP))
+    if (core_udp_open(socket))
 	return;
 
     buf = rrq_packet_buf;
@@ -255,22 +255,22 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode,
 
     timeout_ptr = TimeoutTable;   /* Reset timeout */
 sendreq:
-    net_core_disconnect(socket);
+    core_udp_disconnect(socket);
     timeout = *timeout_ptr++;
     if (!timeout)
 	return;			/* No file available... */
     oldtime = jiffies();
 
-    net_core_connect(socket, url->ip, url->port);
-    net_core_send(socket, rrq_packet_buf, rrq_len);
+    core_udp_connect(socket, url->ip, url->port);
+    core_udp_send(socket, rrq_packet_buf, rrq_len);
 
     /* If the WRITE call fails, we let the timeout take care of it... */
 wait_pkt:
-    net_core_disconnect(socket);
+    core_udp_disconnect(socket);
     for (;;) {
 	buf_len = sizeof(reply_packet_buf);
 
-	err = net_core_recv(socket, reply_packet_buf, &buf_len,
+	err = core_udp_recv(socket, reply_packet_buf, &buf_len,
 			    &src_ip, &src_port);
 	if (err) {
 	    jiffies_t now = jiffies();
@@ -283,8 +283,8 @@ wait_pkt:
 	}
     }
 
-    net_core_disconnect(socket);
-    net_core_connect(socket, src_ip, src_port);
+    core_udp_disconnect(socket);
+    core_udp_connect(socket, src_ip, src_port);
 
     /* filesize <- -1 == unknown */
     inode->size = -1;
@@ -437,7 +437,7 @@ err_reply:
 
 done:
     if (!inode->size)
-	net_core_close(socket);
+	core_udp_close(socket);
 
     return;
 }
diff --git a/core/include/net.h b/core/include/net.h
index 4f6819f..a5dcd72 100644
--- a/core/include/net.h
+++ b/core/include/net.h
@@ -2,32 +2,38 @@
 #define _NET_H
 
 #include <stdint.h>
+#include <stdbool.h>
 #include <stddef.h>
 
-/* Protocol family */
-enum net_core_proto {
-    NET_CORE_TCP,
-    NET_CORE_UDP,
-};
-
 void net_core_init(void);
+void net_parse_dhcp(void);
 
 struct pxe_pvt_inode;
 
-int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto);
-void net_core_close(struct pxe_pvt_inode *socket);
+int core_udp_open(struct pxe_pvt_inode *socket);
+void core_udp_close(struct pxe_pvt_inode *socket);
 
-void net_core_connect(struct pxe_pvt_inode *socket,
+void core_udp_connect(struct pxe_pvt_inode *socket,
 		      uint32_t ip, uint16_t port);
-void net_core_disconnect(struct pxe_pvt_inode *socket);
+void core_udp_disconnect(struct pxe_pvt_inode *socket);
 
-int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
 		  uint32_t *src_ip, uint16_t *src_port);
 
-void net_core_send(struct pxe_pvt_inode *socket,
+void core_udp_send(struct pxe_pvt_inode *socket,
 		   const void *data, size_t len);
 
 void probe_undi(void);
 void pxe_init_isr(void);
 
+struct inode;
+
+int core_tcp_open(struct pxe_pvt_inode *socket);
+int core_tcp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port);
+bool core_tcp_is_connected(struct pxe_pvt_inode *socket);
+int core_tcp_write(struct pxe_pvt_inode *socket, const void *data,
+		   size_t len, bool copy);
+void core_tcp_close_file(struct inode *inode);
+void core_tcp_fill_buffer(struct inode *inode);
+
 #endif /* _NET_H */
diff --git a/core/legacynet/core.c b/core/legacynet/core.c
index 848410c..3ebc8f9 100644
--- a/core/legacynet/core.c
+++ b/core/legacynet/core.c
@@ -20,19 +20,13 @@ const struct url_scheme url_schemes[] = {
  * Open a socket
  *
  * @param:socket, the socket to open
- * @param:proto, the protocol of the new connection
  *
  * @out: error code, 0 on success, -1 on failure
  */
-int net_core_open(struct pxe_pvt_inode *socket __unused,
-		  enum net_core_proto proto)
+int core_udp_open(struct pxe_pvt_inode *socket __unused)
 {
     struct net_private_tftp *priv = &socket->net.tftp;
 
-    /* The legacy stack only supports UDP */
-    if (proto != NET_CORE_UDP)
-	return -1;
-
     /* Allocate local UDP port number */
     priv->localport = get_port();
 
@@ -44,7 +38,7 @@ int net_core_open(struct pxe_pvt_inode *socket __unused,
  *
  * @param:socket, the socket to open
  */
-void net_core_close(struct pxe_pvt_inode *socket)
+void core_udp_close(struct pxe_pvt_inode *socket)
 {
     struct net_private_tftp *priv = &socket->net.tftp;
 
@@ -59,7 +53,7 @@ void net_core_close(struct pxe_pvt_inode *socket)
  * @param:ip, the ip address
  * @param:port, the port number, host-byte order
  */
-void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
+void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
 		      uint16_t port)
 {
     struct net_private_tftp *priv = &socket->net.tftp;
@@ -74,7 +68,7 @@ void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
  *
  * @param:socket, the open socket
  */
-void net_core_disconnect(struct pxe_pvt_inode *socket __unused)
+void core_udp_disconnect(struct pxe_pvt_inode *socket __unused)
 {
 }
 
@@ -88,7 +82,7 @@ void net_core_disconnect(struct pxe_pvt_inode *socket __unused)
  * @out: src_ip, ip address of the data source
  * @out: src_port, port number of the data source, host-byte order
  */
-int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
 		  uint32_t *src_ip, uint16_t *src_port)
 {
     static __lowmem struct s_PXENV_UDP_READ  udp_read;
@@ -126,7 +120,7 @@ int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
  * @param:data, data buffer to send
  * @param:len, size of data bufer
  */
-void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
+void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
 {
     static __lowmem struct s_PXENV_UDP_WRITE udp_write;
     struct net_private_tftp *priv = &socket->net.tftp;
diff --git a/efi/Makefile b/efi/Makefile
index fbfc5d2..272ce0d 100644
--- a/efi/Makefile
+++ b/efi/Makefile
@@ -29,6 +29,10 @@ CORE_OBJS = $(filter-out %hello.o %rawcon.o %plaincon.o %strcasecmp.o %bios.o \
 	%diskio_bios.o %ldlinux-c.o %isolinux-c.o %pxelinux-c.o \
 	$(FILTERED_OBJS),$(CORE_COBJ) $(CORE_SOBJ))
 
+CORE_OBJS += $(addprefix $(OBJ)/../core/, \
+	fs/pxe/pxe.o fs/pxe/tftp.o fs/pxe/urlparse.o fs/pxe/dhcp_option.o \
+	fs/pxe/ftp.o fs/pxe/ftp_readdir.o fs/pxe/http.o fs/pxe/http_readdir.o)
+
 LIB_OBJS = $(addprefix $(objdir)/com32/lib/,$(CORELIBOBJS))
 
 CSRC = $(wildcard $(SRC)/*.c)
diff --git a/efi/efi.h b/efi/efi.h
index c5bd777..9bb0e20 100644
--- a/efi/efi.h
+++ b/efi/efi.h
@@ -29,6 +29,13 @@ struct efi_disk_private {
 	EFI_DISK_IO *dio;
 };
 
+struct efi_binding {
+    EFI_SERVICE_BINDING *binding;
+    EFI_HANDLE parent;
+    EFI_HANDLE child;
+    EFI_HANDLE this;
+};
+
 extern EFI_HANDLE image_handle;
 
 struct screen_info;
@@ -41,4 +48,17 @@ extern void *efi_malloc(size_t, enum heap, size_t);
 extern void *efi_realloc(void *, size_t);
 extern void efi_free(void *);
 
+extern struct efi_binding *efi_create_binding(EFI_GUID *, EFI_GUID *);
+extern void efi_destroy_binding(struct efi_binding *, EFI_GUID *);
+
+static inline EFI_STATUS
+efi_setup_event(EFI_EVENT *ev, EFI_EVENT_NOTIFY func, void *ctx)
+{
+    EFI_STATUS status;
+
+    status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_NOTIFY_SIGNAL,
+			       TPL_CALLBACK, func, ctx, ev);
+    return status;
+}
+
 #endif /* _SYSLINUX_EFI_H */
diff --git a/efi/main.c b/efi/main.c
index 0bd8f22..31f0bff 100644
--- a/efi/main.c
+++ b/efi/main.c
@@ -26,6 +26,86 @@ uint8_t KbdMap[256];
 char aux_seg[256];
 uint16_t BIOSName;
 
+static inline EFI_STATUS
+efi_close_protocol(EFI_HANDLE handle, EFI_GUID *guid, EFI_HANDLE agent,
+		   EFI_HANDLE controller)
+{
+    return uefi_call_wrapper(BS->CloseProtocol, 4, handle,
+			     guid, agent, controller);
+}
+
+struct efi_binding *efi_create_binding(EFI_GUID *bguid, EFI_GUID *pguid)
+{
+    EFI_SERVICE_BINDING *sbp;
+    struct efi_binding *b;
+    EFI_STATUS status;
+    EFI_HANDLE protocol, child, *handles = NULL;
+    UINTN i, nr_handles = 0;
+
+    b = malloc(sizeof(*b));
+    if (!b)
+	return NULL;
+
+    status = LibLocateHandle(ByProtocol, bguid, NULL, &nr_handles, &handles);
+    if (status != EFI_SUCCESS)
+	goto free_binding;
+
+    for (i = 0; i < nr_handles; i++) {
+	status = uefi_call_wrapper(BS->OpenProtocol, 6, handles[i],
+				   bguid, (void **)&sbp,
+				   image_handle, handles[i],
+				   EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (status == EFI_SUCCESS)
+	    break;
+
+	uefi_call_wrapper(BS->CloseProtocol, 4, handles[i], bguid,
+			  image_handle, handles[i]);
+    }
+
+    if (i == nr_handles)
+	goto free_binding;
+
+    child = NULL;
+
+    status = uefi_call_wrapper(sbp->CreateChild, 2, sbp, (EFI_HANDLE *)&child);
+    if (status != EFI_SUCCESS)
+	goto close_protocol;
+
+    status = uefi_call_wrapper(BS->OpenProtocol, 6, child,
+			      pguid, (void **)&protocol,
+			      image_handle, sbp,
+			      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+    if (status != EFI_SUCCESS)
+	goto destroy_child;
+
+    b->parent = handles[i];
+    b->binding = sbp;
+    b->child = child;
+    b->this = protocol;
+
+    return b;
+
+destroy_child:
+    uefi_call_wrapper(sbp->DestroyChild, 2, sbp, child);
+
+close_protocol:
+    uefi_call_wrapper(BS->CloseProtocol, 4, handles[i], bguid,
+		      image_handle, handles[i]);
+
+free_binding:
+    free(b);
+    return NULL;
+}
+
+void efi_destroy_binding(struct efi_binding *b, EFI_GUID *guid)
+{
+    efi_close_protocol(b->child, guid, image_handle, b->binding);
+    uefi_call_wrapper(b->binding->DestroyChild, 2, b->binding, b->child);
+    efi_close_protocol(b->parent, guid, image_handle, b->parent);
+
+    free(b);
+}
+
 #undef kaboom
 void kaboom(void)
 {
@@ -1057,6 +1137,7 @@ static inline void syslinux_register_efi(void)
 
 extern void init(void);
 extern const struct fs_ops vfat_fs_ops;
+extern const struct fs_ops pxe_fs_ops;
 
 char free_high_memory[4096];
 
@@ -1098,7 +1179,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
 {
 	EFI_LOADED_IMAGE *info;
 	EFI_STATUS status = EFI_SUCCESS;
+#if 0
 	const struct fs_ops *ops[] = { &vfat_fs_ops, NULL };
+#else
+	const struct fs_ops *ops[] = { &pxe_fs_ops, NULL };
+#endif
 	unsigned long len = (unsigned long)__bss_end - (unsigned long)__bss_start;
 	static struct efi_disk_private priv;
 	SIMPLE_INPUT_INTERFACE *in;
@@ -1120,10 +1205,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table)
 	}
 
 	/* Use device handle to set up the volume root to proceed with ADV init */
-	if (EFI_ERROR(efi_set_volroot(info->DeviceHandle))) {
-		Print(L"Failed to locate root device to prep for file operations & ADV initialization\n");
-		goto out;
-	}
+	efi_set_volroot(info->DeviceHandle);
+
 	/* setup timer for boot menu system support */
 	status = setup_default_timer(&timer_ev);
 	if (status != EFI_SUCCESS) {
diff --git a/efi/pxe.c b/efi/pxe.c
new file mode 100644
index 0000000..1acfcdc
--- /dev/null
+++ b/efi/pxe.c
@@ -0,0 +1,136 @@
+#include <syslinux/firmware.h>
+#include <syslinux/pxe_api.h>
+#include "efi.h"
+#include "net.h"
+#include "fs/pxe/pxe.h"
+
+const struct url_scheme url_schemes[] = {
+    { "tftp", tftp_open, 0 },
+    { "http", http_open, O_DIRECTORY },
+    { "ftp",  ftp_open,  O_DIRECTORY },
+    { NULL, NULL, 0 },
+};
+
+/**
+ * Network stack-specific initialization
+ */
+void net_core_init(void)
+{
+    http_bake_cookies();
+}
+
+void pxe_init_isr(void) {}
+void gpxe_init(void) {}
+void pxe_idle_init(void) {}
+
+int reset_pxe(void)
+{
+    return 0;
+}
+
+#define DNS_MAX_SERVERS 4		/* Max no of DNS servers */
+uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
+
+__export uint32_t dns_resolv(const char *name)
+{
+    /*
+     * Return failure on an empty input... this can happen during
+     * some types of URL parsing, and this is the easiest place to
+     * check for it.
+     */
+    if (!name || !*name)
+	return 0;
+
+    return 0;
+}
+
+int pxe_init(bool quiet)
+{
+    EFI_HANDLE *handles;
+    EFI_STATUS status;
+    UINTN nr_handles;
+
+    status = LibLocateHandle(ByProtocol, &PxeBaseCodeProtocol,
+			     NULL, &nr_handles, &handles);
+    if (status != EFI_SUCCESS) {
+	if (!quiet)
+	    Print(L"No PXE Base Code Protocol\n");
+	return -1;
+    }
+
+    return 0;
+}
+
+void net_parse_dhcp(void)
+{
+    EFI_PXE_BASE_CODE_MODE *mode;
+    EFI_PXE_BASE_CODE *bc;
+    unsigned int pkt_len = sizeof(EFI_PXE_BASE_CODE_PACKET);
+    EFI_STATUS status;
+    EFI_HANDLE *handles = NULL;
+    UINTN nr_handles = 0;
+    uint8_t hardlen;
+    uint32_t ip;
+    char dst[256];
+
+    status = LibLocateHandle(ByProtocol, &PxeBaseCodeProtocol,
+			 NULL, &nr_handles, &handles);
+    if (status != EFI_SUCCESS)
+	return;
+
+    /* Probably want to use IPv4 protocol to decide which handle to use */
+    status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[0],
+			   &PxeBaseCodeProtocol, (void **)&bc);
+    if (status != EFI_SUCCESS) {
+	Print(L"Failed to lookup PxeBaseCodeProtocol\n");
+    }
+
+    mode = bc->Mode;
+
+    /*
+     * Get the DHCP client identifiers (query info 1)
+     */
+    Print(L"Getting cached packet ");
+    parse_dhcp(&mode->DhcpDiscover.Dhcpv4, 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)
+     */
+    parse_dhcp(&mode->DhcpAck.Dhcpv4, 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
+     */
+    hardlen = mode->DhcpAck.Dhcpv4.BootpHwAddrLen;
+    MAC_len = hardlen > 16 ? 0 : hardlen;
+    MAC_type = mode->DhcpAck.Dhcpv4.BootpHwType;
+    memcpy(MAC, mode->DhcpAck.Dhcpv4.BootpHwAddr, MAC_len);
+
+    /*
+     * Get the boot file and other info. This lives in the CACHED_REPLY
+     * packet (query info 3)
+     */
+    parse_dhcp(&mode->PxeReply.Dhcpv4, pkt_len);
+    Print(L"\n");
+
+    ip = IPInfo.myip;
+    sprintf(dst, "%u.%u.%u.%u",
+        ((const uint8_t *)&ip)[0],
+        ((const uint8_t *)&ip)[1],
+        ((const uint8_t *)&ip)[2],
+        ((const uint8_t *)&ip)[3]);
+
+    Print(L"My IP is %a\n", dst);
+}
diff --git a/efi/tcp.c b/efi/tcp.c
new file mode 100644
index 0000000..51b2f8e
--- /dev/null
+++ b/efi/tcp.c
@@ -0,0 +1,232 @@
+#include "efi.h"
+#include "net.h"
+#include "fs/pxe/pxe.h"
+
+extern EFI_GUID Tcp4ServiceBindingProtocol;
+extern EFI_GUID Tcp4Protocol;
+
+
+extern struct efi_binding *efi_create_binding(EFI_GUID *, EFI_GUID *);
+extern void efi_destroy_binding(struct efi_binding *, EFI_GUID *);
+int core_tcp_open(struct pxe_pvt_inode *socket)
+{
+    struct efi_binding *b;
+
+    b = efi_create_binding(&Tcp4ServiceBindingProtocol, &Tcp4Protocol);
+    if (!b)
+	return -1;
+
+    socket->net.efi.binding = b;
+
+    return 0;
+}
+
+static EFIAPI void null_cb(EFI_EVENT ev, void *context)
+{
+    EFI_TCP4_COMPLETION_TOKEN *token = context;
+
+    (void)ev;
+
+    uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
+}
+
+static int volatile cb_status = -1;
+static EFIAPI void tcp_cb(EFI_EVENT ev, void *context)
+{
+    EFI_TCP4_COMPLETION_TOKEN *token = context;
+
+    (void)ev;
+
+    if (token->Status == EFI_SUCCESS)
+	cb_status = 0;
+    else
+	cb_status = 1;
+}
+
+int core_tcp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port)
+{
+    EFI_TCP4_CONNECTION_TOKEN token;
+    EFI_TCP4_ACCESS_POINT *ap;
+    EFI_TCP4_CONFIG_DATA tdata;
+    struct efi_binding *b = socket->net.efi.binding;
+    EFI_STATUS status;
+    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
+    int rv = -1;
+
+    memset(&tdata, 0, sizeof(tdata));
+
+    ap = &tdata.AccessPoint;
+    memcpy(&ap->StationAddress, &IPInfo.myip, sizeof(IPInfo.myip));
+    memcpy(&ap->SubnetMask, &IPInfo.netmask, sizeof(IPInfo.netmask));
+    memcpy(&ap->RemoteAddress, &ip, sizeof(ip));
+    ap->RemotePort = port;
+    ap->ActiveFlag = TRUE; /* Initiate active open */
+
+    status = uefi_call_wrapper(tcp->Configure, 2, tcp, &tdata);
+    if (status != EFI_SUCCESS)
+	return -1;
+
+    status = efi_setup_event(&token.CompletionToken.Event,
+			    (EFI_EVENT_NOTIFY)tcp_cb, &token.CompletionToken);
+    if (status != EFI_SUCCESS)
+	return -1;
+
+    status = uefi_call_wrapper(tcp->Connect, 2, tcp, &token);
+    if (status != EFI_SUCCESS) {
+	Print(L"Failed to connect: %d\n", status);
+	goto out;
+    }
+
+    while (cb_status == -1)
+	uefi_call_wrapper(tcp->Poll, 1, tcp);
+
+    if (cb_status == 0)
+	rv = 0;
+
+    /* Reset */
+    cb_status = -1;
+
+out:
+    uefi_call_wrapper(BS->CloseEvent, 1, token.CompletionToken.Event);
+    return rv;
+}
+
+bool core_tcp_is_connected(struct pxe_pvt_inode *socket)
+{
+    if (socket->net.efi.binding)
+	return true;
+
+    return false;
+}
+
+int core_tcp_write(struct pxe_pvt_inode *socket, const void *data,
+		   size_t len, bool copy)
+{
+    EFI_TCP4_TRANSMIT_DATA txdata;
+    EFI_TCP4_FRAGMENT_DATA *frag;
+    struct efi_binding *b = socket->net.efi.binding;
+    EFI_TCP4_IO_TOKEN iotoken;
+    EFI_STATUS status;
+    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
+    int rv = -1;
+
+    (void)copy;
+
+    memset(&iotoken, 0, sizeof(iotoken));
+    memset(&txdata, 0, sizeof(txdata));
+
+    txdata.DataLength = len;
+    txdata.FragmentCount = 1;
+
+    frag = &txdata.FragmentTable[0];
+    frag->FragmentLength = len;
+    frag->FragmentBuffer = (void *)data;
+
+    iotoken.Packet.TxData = &txdata;
+
+    status = efi_setup_event(&iotoken.CompletionToken.Event,
+			     (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken);
+    if (status != EFI_SUCCESS)
+	return -1;
+
+    status = uefi_call_wrapper(tcp->Transmit, 2, tcp, &iotoken);
+    if (status != EFI_SUCCESS) {
+	Print(L"tcp transmit failed, %d\n", status);
+	goto out;
+    }
+
+    while (cb_status == -1)
+	uefi_call_wrapper(tcp->Poll, 1, tcp);
+
+    if (cb_status == 0)
+	rv = 0;
+
+    /* Reset */
+    cb_status = -1;
+
+out:
+    uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event);
+    return rv;
+}
+
+void core_tcp_close_file(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    struct efi_binding *b = socket->net.efi.binding;
+    EFI_TCP4_CLOSE_TOKEN token;
+    EFI_STATUS status;
+    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
+
+  if (!socket->tftp_goteof) {
+	memset(&token, 0, sizeof(token));
+
+	status = efi_setup_event(&token.CompletionToken.Event,
+				 (EFI_EVENT_NOTIFY)null_cb,
+				 &token.CompletionToken);
+	if (status != EFI_SUCCESS)
+	    return;
+
+	status = uefi_call_wrapper(tcp->Close, 2, tcp, &token);
+	if (status != EFI_SUCCESS)
+	    Print(L"tcp close failed: %d\n", status);
+    }
+
+    efi_destroy_binding(b, &Tcp4ServiceBindingProtocol);
+    socket->net.efi.binding = NULL;
+}
+
+static char databuf[8192];
+
+void core_tcp_fill_buffer(struct inode *inode)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    struct efi_binding *b = socket->net.efi.binding;
+    EFI_TCP4_IO_TOKEN iotoken;
+    EFI_TCP4_RECEIVE_DATA rxdata;
+    EFI_TCP4_FRAGMENT_DATA *frag;
+    EFI_STATUS status;
+    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
+    void *data;
+    size_t len;
+
+    memset(&iotoken, 0, sizeof(iotoken));
+    memset(&rxdata, 0, sizeof(rxdata));
+
+    status = efi_setup_event(&iotoken.CompletionToken.Event,
+		      (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken);
+    if (status != EFI_SUCCESS)
+	return;
+
+    iotoken.Packet.RxData = &rxdata;
+    rxdata.FragmentCount = 1;
+    rxdata.DataLength = sizeof(databuf);
+    frag = &rxdata.FragmentTable[0];
+    frag->FragmentBuffer = databuf;
+    frag->FragmentLength = sizeof(databuf);
+
+    status = uefi_call_wrapper(tcp->Receive, 2, tcp, &iotoken);
+    if (status == EFI_CONNECTION_FIN) {
+	socket->tftp_goteof = 1;
+	if (inode->size == -1)
+	    inode->size = socket->tftp_filepos;
+	socket->ops->close(inode);
+	goto out;
+    }
+
+    while (cb_status == -1)
+	uefi_call_wrapper(tcp->Poll, 1, tcp);
+
+    /* Reset */
+    cb_status = -1;
+
+    len = frag->FragmentLength;
+    memcpy(databuf, frag->FragmentBuffer, len);
+    data = databuf;
+
+    socket->tftp_dataptr = data;
+    socket->tftp_filepos += len;
+    socket->tftp_bytesleft = len;
+
+out:
+    uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event);
+}
diff --git a/efi/udp.c b/efi/udp.c
new file mode 100644
index 0000000..c762ef3
--- /dev/null
+++ b/efi/udp.c
@@ -0,0 +1,251 @@
+#include <string.h>
+#include <minmax.h>
+#include "efi.h"
+#include "net.h"
+#include "fs/pxe/pxe.h"
+
+extern EFI_GUID Udp4ServiceBindingProtocol, Udp4Protocol;
+
+/*
+ * This UDP binding is configured to operate in promiscuous mode. It is
+ * only used for reading packets. It has no associated state unlike
+ * socket->net.efi.binding, which has a remote IP address and port
+ * number.
+ */
+static struct efi_binding *udp_reader;
+
+/**
+ * Open a socket
+ *
+ * @param:socket, the socket to open
+ *
+ * @out: error code, 0 on success, -1 on failure
+ */
+int core_udp_open(struct pxe_pvt_inode *socket)
+{
+    EFI_UDP4_CONFIG_DATA udata;
+    EFI_STATUS status;
+    EFI_UDP4 *udp;
+
+    (void)socket;
+
+    udp_reader = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
+    if (!udp_reader)
+	return -1;
+
+    udp = (EFI_UDP4 *)udp_reader->this;
+
+    memset(&udata, 0, sizeof(udata));
+    udata.AcceptPromiscuous = TRUE;
+    udata.AcceptAnyPort = TRUE;
+
+    status = uefi_call_wrapper(udp->Configure, 2, udp, &udata);
+    if (status != EFI_SUCCESS) {
+	efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
+	udp_reader = NULL;
+	return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * Close a socket
+ *
+ * @param:socket, the socket to open
+ */
+void core_udp_close(struct pxe_pvt_inode *socket)
+{
+    (void)socket;
+
+    efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
+    udp_reader = NULL;
+}
+
+/**
+ * Establish a connection on an open socket
+ *
+ * @param:socket, the open socket
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
+		      uint16_t port)
+{
+    EFI_UDP4_CONFIG_DATA udata;
+    struct efi_binding *b;
+    EFI_STATUS status;
+    EFI_UDP4 *udp;
+
+    b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
+    if (!b)
+	return;
+
+    socket->net.efi.binding = b;
+
+    udp = (EFI_UDP4 *)b->this;
+
+    memset(&udata, 0, sizeof(udata));
+
+    memcpy(&udata.StationAddress, &IPInfo.myip, sizeof(IPInfo.myip));
+    memcpy(&udata.SubnetMask, &IPInfo.netmask, sizeof(IPInfo.netmask));
+    memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
+    udata.RemotePort = port;
+    udata.AcceptPromiscuous = TRUE;
+
+    status = uefi_call_wrapper(udp->Configure, 2, udp, &udata);
+    if (status != EFI_SUCCESS)
+	Print(L"Failed to configure UDP: %d\n", status);
+}
+
+/**
+ * Tear down a connection on an open socket
+ *
+ * @param:socket, the open socket
+ */
+void core_udp_disconnect(struct pxe_pvt_inode *socket)
+{
+    struct efi_binding *b;
+
+    if (!socket->net.efi.binding)
+	return;
+
+    b = socket->net.efi.binding;
+    efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
+    socket->net.efi.binding = NULL;
+}
+
+static int volatile cb_status = -1;
+static EFIAPI void udp4_cb(EFI_EVENT event, void *context)
+{
+    (void)event;
+
+    EFI_UDP4_COMPLETION_TOKEN *token = context;
+
+    if (token->Status == EFI_SUCCESS)
+	cb_status = 0;
+    else
+	cb_status = 1;
+}
+
+/**
+ * Read data from the network stack
+ *
+ * @param:socket, the open socket
+ * @param:buf, location of buffer to store data
+ * @param:buf_len, size of buffer
+
+ * @out: src_ip, ip address of the data source
+ * @out: src_port, port number of the data source, host-byte order
+ */
+int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+		  uint32_t *src_ip, uint16_t *src_port)
+{
+    EFI_UDP4_COMPLETION_TOKEN token;
+    EFI_UDP4_FRAGMENT_DATA *frag;
+    EFI_UDP4_RECEIVE_DATA *rxdata;
+    struct efi_binding *b;
+    EFI_STATUS status;
+    EFI_UDP4 *udp;
+    size_t size;
+    int rv = -1;
+
+    (void)socket;
+
+    b = udp_reader;
+    udp = (EFI_UDP4 *)b->this;
+
+    status = efi_setup_event(&token.Event, (EFI_EVENT_NOTIFY)udp4_cb,
+			     &token);
+    if (status != EFI_SUCCESS)
+	return -1;
+
+    status = uefi_call_wrapper(udp->Receive, 2, udp, &token);
+    if (status != EFI_SUCCESS)
+	goto bail;
+
+    while (cb_status == -1)
+	uefi_call_wrapper(udp->Poll, 1, udp);
+
+    if (cb_status == 0)
+	rv = 0;
+
+    /* Reset */
+    cb_status = -1;
+
+    rxdata = token.Packet.RxData;
+    frag = &rxdata->FragmentTable[0];
+
+    size = min(frag->FragmentLength, *buf_len);
+    memcpy(buf, frag->FragmentBuffer, size);
+    *buf_len = size;
+
+    memcpy(src_port, &rxdata->UdpSession.SourcePort, sizeof(*src_port));
+    memcpy(src_ip, &rxdata->UdpSession.SourceAddress, sizeof(*src_ip));
+
+    uefi_call_wrapper(BS->SignalEvent, 1, rxdata->RecycleSignal);
+
+bail:
+    uefi_call_wrapper(BS->CloseEvent, 1, token.Event);
+    return rv;
+}
+
+/**
+ * Send a UDP packet.
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ */
+void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
+{
+    EFI_UDP4_COMPLETION_TOKEN *token;
+    EFI_UDP4_TRANSMIT_DATA *txdata;
+    EFI_UDP4_FRAGMENT_DATA *frag;
+    struct efi_binding *b = socket->net.efi.binding;
+    EFI_STATUS status;
+    EFI_UDP4 *udp = (EFI_UDP4 *)b->this;
+
+    token = zalloc(sizeof(*token));
+    if (!token)
+	return;
+
+    txdata = zalloc(sizeof(*txdata));
+    if (!txdata) {
+	free(token);
+	return;
+    }
+
+    status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
+			     token);
+    if (status != EFI_SUCCESS)
+	goto bail;
+
+    txdata->UdpSessionData = NULL;
+    txdata->GatewayAddress = NULL;
+    txdata->DataLength = len;
+    txdata->FragmentCount = 1;
+    frag = &txdata->FragmentTable[0];
+
+    frag->FragmentLength = len;
+    frag->FragmentBuffer = (void *)data;
+
+    token->Packet.TxData = txdata;
+
+    status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
+    if (status != EFI_SUCCESS)
+	goto close;
+
+    while (cb_status == -1)
+	uefi_call_wrapper(udp->Poll, 1, udp);
+
+    /* Reset */
+    cb_status = -1;
+
+close:
+    uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
+
+bail:
+    free(txdata);
+    free(token);
+}
diff --git a/mk/efi.mk b/mk/efi.mk
index 0281795..24cca8c 100644
--- a/mk/efi.mk
+++ b/mk/efi.mk
@@ -39,7 +39,7 @@ CFLAGS = -I$(EFIINC) -I$(EFIINC)/$(EFI_SUBARCH) \
 		-Wall -I$(com32)/include -I$(com32)/include/sys \
 		-I$(core)/include -I$(core)/ $(CARCHOPT) \
 		-I$(com32)/lib/ -I$(com32)/libutil/include -std=gnu99 \
-		-DELF_DEBUG -DSYSLINUX_EFI \
+		-DELF_DEBUG -DSYSLINUX_EFI -I$(objdir) \
 		$(GCCWARN) -D__COM32__ -mno-red-zone
 
 # gnuefi sometimes installs these under a gnuefi/ directory, and sometimes not
diff --git a/mk/lib.mk b/mk/lib.mk
index c39b0c9..596e928 100644
--- a/mk/lib.mk
+++ b/mk/lib.mk
@@ -63,7 +63,7 @@ LIBOTHER_OBJS = \
 	lrand48.o stack.o memccpy.o memchr.o 		\
 	mempcpy.o memmem.o memmove.o memswap.o	\
 	perror.o qsort.o seed48.o \
-	srand48.o sscanf.o strcasecmp.o					\
+	srand48.o sscanf.o 						\
 	strerror.o errlist.o		\
 	strnlen.o							\
 	strncat.o strndup.o		\
@@ -185,7 +185,7 @@ CORELIBOBJS = \
 	memcpy.o memset.o memcmp.o printf.o strncmp.o vfprintf.o 	\
 	strlen.o vsnprintf.o snprintf.o stpcpy.o strcmp.o strdup.o 	\
 	strcpy.o strncpy.o setjmp.o fopen.o fread.o fread2.o puts.o 	\
-	strtoul.o strntoumax.o 						\
+	strtoul.o strntoumax.o strcasecmp.o 				\
 	sprintf.o strlcat.o strchr.o strlcpy.o strncasecmp.o ctypes.o 	\
 	fputs.o fwrite2.o fwrite.o fgetc.o fclose.o lmalloc.o 		\
 	sys/err_read.o sys/err_write.o sys/null_read.o 			\


More information about the Syslinux-commits mailing list