diff options
-rw-r--r-- | core/Makefile | 9 | ||||
-rw-r--r-- | core/fs/pxe/bios.c | 403 | ||||
-rw-r--r-- | core/fs/pxe/core.c | 124 | ||||
-rw-r--r-- | core/fs/pxe/ftp.c | 39 | ||||
-rw-r--r-- | core/fs/pxe/http.c | 30 | ||||
-rw-r--r-- | core/fs/pxe/pxe.c | 396 | ||||
-rw-r--r-- | core/fs/pxe/pxe.h | 10 | ||||
-rw-r--r-- | core/fs/pxe/tcp.c | 59 | ||||
-rw-r--r-- | core/fs/pxe/tftp.c | 26 | ||||
-rw-r--r-- | core/include/net.h | 30 | ||||
-rw-r--r-- | core/legacynet/core.c | 18 | ||||
-rw-r--r-- | efi/Makefile | 4 | ||||
-rw-r--r-- | efi/efi.h | 20 | ||||
-rw-r--r-- | efi/main.c | 91 | ||||
-rw-r--r-- | efi/pxe.c | 136 | ||||
-rw-r--r-- | efi/tcp.c | 232 | ||||
-rw-r--r-- | efi/udp.c | 251 | ||||
-rw-r--r-- | mk/efi.mk | 2 | ||||
-rw-r--r-- | mk/lib.mk | 4 |
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(®s, 0, sizeof regs); + regs.eax.w[0] = 0x5650; + call16(pxe_int1a, ®s, ®s); + 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(®s, 0, sizeof regs); - regs.eax.w[0] = 0x5650; - call16(pxe_int1a, ®s, ®s); - 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) @@ -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 */ @@ -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); +} @@ -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 @@ -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 \ |