aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2013-03-22 14:54:09 +0000
committerMatt Fleming <matt.fleming@intel.com>2013-04-23 15:30:17 +0100
commitfe283b78c973268f2d1f0309826ceeb5c9e8978d (patch)
treede90b141427ab9222bb3e82afef6f880778b3cf2
parentbb229372b2c3f9fbb0cdd0a7221a6cc4aba5d2ed (diff)
downloadsyslinux-6.00-pre5.tar.gz
syslinux-6.00-pre5.tar.xz
syslinux-6.00-pre5.zip
efi: Add network supportsyslinux-6.00-pre5
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@intel.com>
-rw-r--r--core/Makefile9
-rw-r--r--core/fs/pxe/bios.c403
-rw-r--r--core/fs/pxe/core.c124
-rw-r--r--core/fs/pxe/ftp.c39
-rw-r--r--core/fs/pxe/http.c30
-rw-r--r--core/fs/pxe/pxe.c396
-rw-r--r--core/fs/pxe/pxe.h10
-rw-r--r--core/fs/pxe/tcp.c59
-rw-r--r--core/fs/pxe/tftp.c26
-rw-r--r--core/include/net.h30
-rw-r--r--core/legacynet/core.c18
-rw-r--r--efi/Makefile4
-rw-r--r--efi/efi.h20
-rw-r--r--efi/main.c91
-rw-r--r--efi/pxe.c136
-rw-r--r--efi/tcp.c232
-rw-r--r--efi/udp.c251
-rw-r--r--mk/efi.mk2
-rw-r--r--mk/lib.mk4
19 files changed, 1322 insertions, 562 deletions
diff --git a/core/Makefile b/core/Makefile
index db295a7a..87cf44b4 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 00000000..81aa715c
--- /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 e330ba82..e6bbee9b 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 c2d155ae..06c232e6 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 950c8bbc..57223728 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 3f68e969..4591edc9 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 3f511fd9..f4b9b6ef 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 576aa293..0fb6efd0 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 9b755c93..b5dc72e4 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 4f6819f9..a5dcd724 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 848410c6..3ebc8f97 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 fbfc5d26..272ce0d7 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 c5bd7771..9bb0e20c 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 0bd8f22f..31f0bff7 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 00000000..1acfcdc5
--- /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 00000000..51b2f8eb
--- /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 00000000..c762ef38
--- /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 02817953..24cca8c1 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 c39b0c9e..596e9284 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 \