diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | core/Makefile | 63 | ||||
-rw-r--r-- | core/fs/pxe/core.c | 207 | ||||
-rw-r--r-- | core/fs/pxe/ftp.c | 32 | ||||
-rw-r--r-- | core/fs/pxe/http.c | 8 | ||||
-rw-r--r-- | core/fs/pxe/pxe.c | 36 | ||||
-rw-r--r-- | core/fs/pxe/pxe.h | 44 | ||||
-rw-r--r-- | core/fs/pxe/tcp.c | 28 | ||||
-rw-r--r-- | core/fs/pxe/tftp.c | 128 | ||||
-rw-r--r-- | core/include/net.h | 33 | ||||
-rw-r--r-- | core/legacynet/core.c | 186 | ||||
-rw-r--r-- | core/legacynet/dnsresolv.c | 353 | ||||
-rw-r--r-- | core/legacynet/idle.c | 112 | ||||
-rw-r--r-- | core/legacynet/portnum.c | 68 | ||||
-rw-r--r-- | core/pxelinux.asm | 2 |
15 files changed, 1129 insertions, 178 deletions
@@ -45,7 +45,8 @@ INSTALLABLE_MODULES = $(MODULES) BTARGET = version.gen version.h version.mk BOBJECTS = $(BTARGET) \ mbr/*.bin \ - core/pxelinux.0 core/isolinux.bin core/isolinux-debug.bin \ + core/pxelinux.0 core/lpxelinux.0 \ + core/isolinux.bin core/isolinux-debug.bin \ gpxe/gpxelinux.0 dos/syslinux.com \ win32/syslinux.exe win64/syslinux64.exe \ dosutil/*.com dosutil/*.sys \ @@ -72,7 +73,7 @@ INSTALL_SBIN = extlinux/extlinux # Things to install in /usr/lib/syslinux INSTALL_AUX = core/pxelinux.0 gpxe/gpxelinux.0 gpxe/gpxelinuxk.0 \ core/isolinux.bin core/isolinux-debug.bin \ - dos/syslinux.com \ + dos/syslinux.com core/lpxelinux.0 \ mbr/*.bin $(INSTALLABLE_MODULES) INSTALL_AUX_OPT = win32/syslinux.exe win64/syslinux64.exe INSTALL_DIAG = diag/mbr/handoff.bin \ @@ -85,7 +86,7 @@ INSTALLSUBDIRS = com32 utils dosutil EXTBOOTINSTALL = $(INSTALLABLE_MODULES) # Things to install in /tftpboot -NETINSTALLABLE = core/pxelinux.0 gpxe/gpxelinux.0 \ +NETINSTALLABLE = core/pxelinux.0 gpxe/gpxelinux.0 core/lpxelinux.0 \ $(INSTALLABLE_MODULES) all: diff --git a/core/Makefile b/core/Makefile index 03c1e66c..1890b244 100644 --- a/core/Makefile +++ b/core/Makefile @@ -26,7 +26,7 @@ include $(MAKEDIR)/embedded.mk OPTFLAGS = INCLUDES = -I./include -I$(com32)/include -I$(com32)/lib \ - -I./lwip/src/include -I./lwip/src/include/ipv4 + -I./lwip/src/include -I./lwip/src/include/ipv4 -I./fs/pxe # This is very similar to cp437; technically it's for Norway and Denmark, # but it's unlikely the characters that are different will be used in @@ -36,7 +36,7 @@ CODEPAGE = cp865 # The targets to build in this directory... BTARGET = kwdhash.gen \ ldlinux.bss ldlinux.sys ldlinux.bin \ - isolinux.bin isolinux-debug.bin pxelinux.0 + isolinux.bin isolinux-debug.bin pxelinux.0 lpxelinux.0 # All primary source files for the main syslinux files NASMSRC := $(wildcard *.asm) @@ -50,13 +50,32 @@ ALLSRC = $(NASMSRC) $(NASMHDR) $(CSRC) $(SSRC) $(CHDR) $(OTHERSRC) COBJ := $(patsubst %.c,%.o,$(CSRC)) SOBJ := $(patsubst %.S,%.o,$(SSRC)) -# Don't include console objects -COBJS = $(filter-out ./rawcon.o ./plaincon.o,$(COBJ)) +# To make this compatible with the following $(filter-out), make sure +# we prefix everything with ./ +CORE_PXE_CSRC = \ + $(addprefix ./fs/pxe/, dhcp_option.c pxe.c tftp.c urlparse.c) + +LPXELINUX_CSRC = $(CORE_PXE_CSRC) \ + $(shell find ./lwip -name '*.c' -print) \ + $(addprefix ./fs/pxe/, \ + core.c dnsresolv.c ftp.c ftp_readdir.c gpxeurl.c http.c \ + http_readdir.c idle.c isr.c tcp.c) + +PXELINUX_CSRC = $(CORE_PXE_CSRC) \ + $(shell find ./legacynet -name '*.c' -print) + +LPXELINUX_OBJS = $(LPXELINUX_CSRC:%.c=%.o) +PXELINUX_OBJS = $(PXELINUX_CSRC:%.c=%.o) + +# Don't include console and network stack specific objects +FILTER_OBJS = ./rawcon.o ./plaincon.o $(LPXELINUX_OBJS) $(PXELINUX_OBJS) +COBJS = $(filter-out $(FILTER_OBJS),$(COBJ)) +SOBJS = $(filter-out $(FILTER_OBJS),$(SOBJ)) LIB = libcom32.a LIBS = $(LIB) --whole-archive $(com32)/lib/libcom32core.a LIBDEP = $(filter-out -% %start%,$(LIBS)) -LIBOBJS = $(COBJS) $(SOBJ) +LIBOBJS = $(COBJS) $(SOBJS) NASMDEBUG = -g -F dwarf NASMOPT += $(NASMDEBUG) @@ -96,7 +115,8 @@ kwdhash.gen: keywords genhash.pl -DHEXDATE="$(HEXDATE)" \ -l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $< -AUXLIBS = libisolinux.a libisolinux-debug.a libldlinux.a libpxelinux.a +AUXLIBS = libisolinux.a libisolinux-debug.a libldlinux.a \ + libpxelinux.a liblpxelinux.a %.elf: %.o $(LIBDEP) syslinux.ld $(AUXLIBS) $(LD) $(LDFLAGS) -Bsymbolic -pie -E --hash-style=gnu -T syslinux.ld -M -o $@ $< \ @@ -113,8 +133,20 @@ libisolinux.a: rawcon.o libisolinux-debug.a: libisolinux.a cp $^ $@ -libpxelinux.a: libisolinux.a - cp $^ $@ +# Legacy network stack +libpxelinux.a: rawcon.o $(PXELINUX_OBJS) + rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + rm $(PXELINUX_OBJS) + +# LwIP network stack +liblpxelinux.a: rawcon.o + $(MAKE) CFLAGS="$(CFLAGS) -DIS_LPXELINUX" $(LPXELINUX_OBJS) + rm -f $@ + $(AR) cq $@ $^ $(LPXELINUX_OBJS) + $(RANLIB) $@ + rm $(LPXELINUX_OBJS) libldlinux.a: plaincon.o rm -f $@ @@ -126,9 +158,24 @@ $(LIB): $(LIBOBJS) $(AR) cq $@ $^ $(RANLIB) $@ +pxelinux.o: pxelinux.asm kwdhash.gen ../version.gen + $(NASM) -f elf $(NASMOPT) -DDATE_STR="'$(DATE)'" \ + -DHEXDATE="$(HEXDATE)" \ + -DIS_LPXELINUX=0 \ + -l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $< + pxelinux.0: pxelinux.bin cp -f $< $@ +lpxelinux.o: pxelinux.asm kwdhash.gen ../version.gen + $(NASM) -f elf $(NASMOPT) -DDATE_STR="'$(DATE)'" \ + -DHEXDATE="$(HEXDATE)" \ + -DIS_LPXELINUX=1 \ + -l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $< + +lpxelinux.0: lpxelinux.bin + cp -f $< $@ + ldlinux.bss: ldlinux.bin dd if=$< of=$@ bs=512 count=1 diff --git a/core/fs/pxe/core.c b/core/fs/pxe/core.c new file mode 100644 index 00000000..9246f669 --- /dev/null +++ b/core/fs/pxe/core.c @@ -0,0 +1,207 @@ +#include <syslinux/pxe_api.h> +#include <lwip/api.h> +#include <lwip/tcpip.h> +#include <lwip/dns.h> +#include <core.h> +#include <net.h> +#include "pxe.h" + +/** + * 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) +{ + struct net_private *priv = &socket->private; + 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); + if (!priv->conn) + return -1; + + priv->conn->recv_timeout = 15; /* A 15 ms recv timeout... */ + err = netconn_bind(priv->conn, NULL, 0); + if (err) { + printf("netconn_bind error %d\n", err); + return -1; + } + + return 0; +} + +/** + * Close a socket + * + * @param:socket, the socket to open + */ +void net_core_close(struct pxe_pvt_inode *socket) +{ + struct net_private *priv = &socket->private; + + if (priv->conn) { + netconn_delete(priv->conn); + priv->conn = 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 net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip, + uint16_t port) +{ + struct net_private *priv = &socket->private; + struct ip_addr addr; + + addr.addr = ip; + netconn_connect(priv->conn, &addr, port); +} + +/** + * Tear down a connection on an open socket + * + * @param:socket, the open socket + */ +void net_core_disconnect(struct pxe_pvt_inode *socket) +{ + struct net_private *priv = &socket->private; + netconn_disconnect(priv->conn); +} + +/** + * 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 net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len, + uint32_t *src_ip, uint16_t *src_port) +{ + struct net_private *priv = &socket->private; + struct netbuf *nbuf; + u16_t nbuf_len; + int err; + + err = netconn_recv(priv->conn, &nbuf); + if (err) + return err; + + if (!nbuf) + return -1; + + *src_ip = netbuf_fromaddr(nbuf)->addr; + *src_port = netbuf_fromport(nbuf); + + netbuf_first(nbuf); /* XXX needed? */ + nbuf_len = netbuf_len(nbuf); + if (nbuf_len <= *buf_len) + netbuf_copy(nbuf, buf, nbuf_len); + else + nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ + netbuf_delete(nbuf); + + *buf_len = nbuf_len; + return 0; +} + +/** + * Send a UDP packet. + * + * @param:socket, the open socket + * @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) +{ + struct netconn *conn = socket->private.conn; + struct netbuf *nbuf; + void *pbuf; + int err; + + nbuf = netbuf_new(); + if (!nbuf) { + printf("netbuf allocation error\n"); + return; + } + + pbuf = netbuf_alloc(nbuf, len); + if (!pbuf) { + printf("pbuf allocation error\n"); + goto out; + } + + memcpy(pbuf, data, len); + + err = netconn_send(conn, nbuf); + if (err) { + printf("netconn_send error %d\n", err); + goto out; + } + +out: + netbuf_delete(nbuf); +} + +/** + * Network stack-specific initialization + */ +void net_core_init(void) +{ + int err; + int i; + + http_bake_cookies(); + + /* Initialize lwip */ + tcpip_init(NULL, NULL); + + /* Start up the undi driver for lwip */ + err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway); + if (err) { + printf("undiif driver failed to start: %d\n", err); + kaboom(); + } + + for (i = 0; i < DNS_MAX_SERVERS; i++) { + /* Transfer the DNS information to lwip */ + dns_setserver(i, (struct ip_addr *)&dns_server[i]); + } +} + +void probe_undi(void) +{ + /* Probe UNDI information */ + pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info); + pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface); + + printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n", + pxe_undi_info.BaseIo, pxe_undi_info.IntNumber, + pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType, + pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags); +} + diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 50c92577..5d5a6f03 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -69,7 +69,7 @@ static int ftp_cmd_response(struct inode *inode, const char *cmd, *q++ = '\n'; cmd_len += 2; - err = netconn_write(socket->conn, cmd_buf, cmd_len, NETCONN_COPY); + err = netconn_write(socket->private.conn, cmd_buf, cmd_len, NETCONN_COPY); if (err) return -1; } @@ -166,7 +166,7 @@ static void ftp_close_file(struct inode *inode) int resp; ctlsock = socket->ctl ? PVT(socket->ctl) : NULL; - if (ctlsock->conn) { + if (ctlsock->private.conn) { resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL); while (resp == 226) { resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL); @@ -209,11 +209,11 @@ 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->conn = netconn_new(NETCONN_TCP); - if (!ctlsock->conn) + ctlsock->private.conn = netconn_new(NETCONN_TCP); + if (!ctlsock->private.conn) goto err_free; addr.addr = url->ip; - err = netconn_connect(ctlsock->conn, &addr, url->port); + err = netconn_connect(ctlsock->private.conn, &addr, url->port); if (err) goto err_delete; @@ -248,10 +248,10 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, if (resp != 227 || pasv_bytes != 6) goto err_disconnect; - socket->conn = netconn_new(NETCONN_TCP); - if (!socket->conn) + socket->private.conn = netconn_new(NETCONN_TCP); + if (!socket->private.conn) goto err_disconnect; - err = netconn_connect(socket->conn, (struct ip_addr *)&pasv_data[0], + err = netconn_connect(socket->private.conn, (struct ip_addr *)&pasv_data[0], ntohs(*(uint16_t *)&pasv_data[4])); if (err) goto err_disconnect; @@ -266,15 +266,15 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, return; /* Sucess! */ err_disconnect: - if (ctlsock->conn) - netconn_write(ctlsock->conn, "QUIT\r\n", 6, NETCONN_NOCOPY); - if (socket->conn) - netconn_delete(socket->conn); - if (ctlsock->buf) - netbuf_delete(ctlsock->buf); + if (ctlsock->private.conn) + netconn_write(ctlsock->private.conn, "QUIT\r\n", 6, NETCONN_NOCOPY); + if (socket->private.conn) + netconn_delete(socket->private.conn); + if (ctlsock->private.buf) + netbuf_delete(ctlsock->private.buf); err_delete: - if (ctlsock->conn) - netconn_delete(ctlsock->conn); + if (ctlsock->private.conn) + netconn_delete(ctlsock->private.conn); err_free: free_socket(socket->ctl); } diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index d5022eb2..9bf0c92e 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -191,8 +191,8 @@ void http_open(struct url_info *url, int flags, struct inode *inode, inode->size = content_length = -1; /* Start the http connection */ - socket->conn = netconn_new(NETCONN_TCP); - if (!socket->conn) { + socket->private.conn = netconn_new(NETCONN_TCP); + if (!socket->private.conn) { printf("netconn_new failed\n"); return; } @@ -201,7 +201,7 @@ void http_open(struct url_info *url, int flags, struct inode *inode, if (!url->port) url->port = HTTP_PORT; - err = netconn_connect(socket->conn, &addr, url->port); + err = netconn_connect(socket->private.conn, &addr, url->port); if (err) { printf("netconn_connect error %d\n", err); goto fail; @@ -225,7 +225,7 @@ 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->conn, header_buf, + err = netconn_write(socket->private.conn, header_buf, header_bytes, NETCONN_NOCOPY); if (err) { printf("netconn_write error %d\n", err); diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index ff8a0b33..9f18f282 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -7,14 +7,11 @@ #include <minmax.h> #include <fcntl.h> #include <sys/cpu.h> -#include <lwip/api.h> -#include <lwip/dns.h> -#include <lwip/tcpip.h> -#include <lwip/opt.h> #include "pxe.h" #include "thread.h" #include "url.h" #include "tftp.h" +#include <net.h> __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info; __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface; @@ -38,8 +35,10 @@ static struct url_scheme { int ok_flags; } url_schemes[] = { { "tftp", tftp_open, 0 }, +#ifdef IS_LPXELINUX { "http", http_open, O_DIRECTORY }, { "ftp", ftp_open, O_DIRECTORY }, +#endif { NULL, NULL, 0 }, }; #define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY) @@ -772,14 +771,7 @@ static int pxe_init(bool quiet) real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */ - /* Probe UNDI information */ - pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info); - pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface); - - printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n", - pxe_undi_info.BaseIo, pxe_undi_info.IntNumber, - pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType, - pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags); + probe_undi(); return 0; } @@ -809,9 +801,7 @@ static void gpxe_init(void) */ static void network_init(void) { - int err; int pkt_len; - int i; struct bootp_t *bp; const size_t dhcp_max_packet = 4096; @@ -870,9 +860,8 @@ static void network_init(void) if (have_uuid) sysappend_set_uuid(uuid); ip_init(); - /* print_sysappend(); */ - http_bake_cookies(); + /* print_sysappend(); */ /* * Check to see if we got any PXELINUX-specific DHCP options; in particular, * if we didn't get the magic enable, do not recognize any other options. @@ -880,20 +869,7 @@ static void network_init(void) if ((DHCPMagic & 1) == 0) DHCPMagic = 0; - /* Initialize lwip */ - tcpip_init(NULL, NULL); - - /* Start up the undi driver for lwip */ - err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway); - if (err) { - printf("undiif driver failed to start: %d\n", err); - kaboom(); - } - - for (i = 0; i < DNS_MAX_SERVERS; i++) { - /* Transfer the DNS information to lwip */ - dns_setserver(i, (struct ip_addr *)&dns_server[i]); - } + net_core_init(); } /* diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 4a43a9db..2d4be3b1 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -114,19 +114,29 @@ struct pxe_conn_ops { int (*readdir)(struct inode *inode, struct dirent *dirent); }; -struct pxe_pvt_inode { +struct net_private { +#ifdef IS_LPXELINUX struct netconn *conn; /* lwip network connection */ struct netbuf *buf; /* lwip cached buffer */ - uint16_t tftp_remoteport; /* Remote port number */ - uint32_t tftp_filepos; /* bytes downloaded (including buffer) */ - uint32_t tftp_blksize; /* Block size for this connection(*) */ - uint16_t tftp_bytesleft; /* Unclaimed data bytes */ - uint16_t tftp_lastpkt; /* Sequence number of last packet (HBO) */ - char *tftp_dataptr; /* Pointer to available data */ - uint8_t tftp_goteof; /* 1 if the EOF packet received */ - uint8_t tftp_unused[3]; /* Currently unused */ - char *tftp_pktbuf; /* Packet buffer */ - struct inode *ctl; /* Control connection (for FTP) */ +#else + uint16_t tftp_localport; /* Local port number (0=not in use) */ + uint32_t tftp_remoteip; /* Remote IP address (0 = disconnected) */ +#endif + +}; + +struct pxe_pvt_inode { + struct net_private private; /* Network stack private data */ + uint16_t tftp_remoteport; /* Remote port number */ + uint32_t tftp_filepos; /* bytes downloaded (including buffer) */ + uint32_t tftp_blksize; /* Block size for this connection(*) */ + uint16_t tftp_bytesleft; /* Unclaimed data bytes */ + uint16_t tftp_lastpkt; /* Sequence number of last packet (HBO) */ + char *tftp_dataptr; /* Pointer to available data */ + uint8_t tftp_goteof; /* 1 if the EOF packet received */ + uint8_t tftp_unused[3]; /* Currently unused */ + char *tftp_pktbuf; /* Packet buffer */ + struct inode *ctl; /* Control connection (for FTP) */ const struct pxe_conn_ops *ops; }; @@ -176,6 +186,18 @@ extern uint8_t uuid_type; extern uint8_t uuid[]; /* + * Compute the suitable gateway for a specific route -- too many + * vendor PXE stacks don't do this correctly... + */ +static inline uint32_t gateway(uint32_t ip) +{ + if ((ip ^ IPInfo.myip) & IPInfo.netmask) + return IPInfo.gateway; + else + return 0; +} + +/* * functions */ diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c index ec7679e7..528fbce8 100644 --- a/core/fs/pxe/tcp.c +++ b/core/fs/pxe/tcp.c @@ -25,13 +25,13 @@ void tcp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); - if (socket->conn) { - netconn_delete(socket->conn); - socket->conn = NULL; + if (socket->private.conn) { + netconn_delete(socket->private.conn); + socket->private.conn = NULL; } - if (socket->buf) { - netbuf_delete(socket->buf); - socket->buf = NULL; + if (socket->private.buf) { + netbuf_delete(socket->private.buf); + socket->private.buf = NULL; } } @@ -43,16 +43,16 @@ void tcp_fill_buffer(struct inode *inode) err_t err; /* Clean up or advance an inuse netbuf */ - if (socket->buf) { - if (netbuf_next(socket->buf) < 0) { - netbuf_delete(socket->buf); - socket->buf = NULL; + if (socket->private.buf) { + if (netbuf_next(socket->private.buf) < 0) { + netbuf_delete(socket->private.buf); + socket->private.buf = NULL; } } /* If needed get a new netbuf */ - if (!socket->buf) { - err = netconn_recv(socket->conn, &(socket->buf)); - if (!socket->buf || err) { + if (!socket->private.buf) { + err = netconn_recv(socket->private.conn, &(socket->private.buf)); + if (!socket->private.buf || err) { socket->tftp_goteof = 1; if (inode->size == -1) inode->size = socket->tftp_filepos; @@ -61,7 +61,7 @@ void tcp_fill_buffer(struct inode *inode) } } /* Report the current fragment of the netbuf */ - err = netbuf_data(socket->buf, &data, &len); + err = netbuf_data(socket->private.buf, &data, &len); if (err) { printf("netbuf_data err: %d\n", err); kaboom(); diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index f6ea2974..9b755c93 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -1,10 +1,10 @@ #include <minmax.h> -#include <lwip/api.h> +#include <net.h> #include "pxe.h" #include "url.h" #include "tftp.h" -static const uint8_t TimeoutTable[] = { +const uint8_t TimeoutTable[] = { 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0 }; @@ -38,43 +38,7 @@ static void tftp_close_file(struct inode *inode) if (!socket->tftp_goteof) { tftp_error(inode, 0, "No error, file close"); } - if (socket->conn) { - netconn_delete(socket->conn); - socket->conn = NULL; - } -} - -/* - * Send a UDP packet. - */ -static void udp_send(struct netconn *conn, const void *data, size_t len) -{ - struct netbuf *nbuf; - void *pbuf; - int err; - - nbuf = netbuf_new(); - if (!nbuf) { - printf("netbuf allocation error\n"); - return; - } - - pbuf = netbuf_alloc(nbuf, len); - if (!pbuf) { - printf("pbuf allocation error\n"); - goto out; - } - - memcpy(pbuf, data, len); - - err = netconn_send(conn, nbuf); - if (err) { - printf("netconn_send error %d\n", err); - goto out; - } - -out: - netbuf_delete(nbuf); + net_core_close(socket); } /** @@ -100,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'; - udp_send(socket->conn, &err_buf, 4 + len + 1); + net_core_send(socket, &err_buf, 4 + len + 1); } /** @@ -119,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); - udp_send(socket->conn, ack_packet_buf, 4); + net_core_send(socket, ack_packet_buf, 4); } /* @@ -135,10 +99,11 @@ static void tftp_get_packet(struct inode *inode) uint16_t serial; jiffies_t oldtime; struct tftp_packet *pkt = NULL; - struct netbuf *nbuf; - u16_t nbuf_len; + uint16_t buf_len; struct pxe_pvt_inode *socket = PVT(inode); - err_t err; + uint16_t src_port; + uint32_t src_ip; + int err; /* * Start by ACKing the previous packet; this should cause @@ -152,8 +117,10 @@ static void tftp_get_packet(struct inode *inode) ack_packet(inode, socket->tftp_lastpkt); while (timeout) { - err = netconn_recv(socket->conn, &nbuf); - if (!nbuf || err) { + buf_len = socket->tftp_blksize + 4; + err = net_core_recv(socket, socket->tftp_pktbuf, &buf_len, + &src_ip, &src_port); + if (err) { jiffies_t now = jiffies(); if (now-oldtime >= timeout) { @@ -166,15 +133,7 @@ static void tftp_get_packet(struct inode *inode) continue; } - netbuf_first(nbuf); - nbuf_len = 0; - nbuf_len = netbuf_len(nbuf); - if (nbuf_len <= socket->tftp_blksize + 4) - netbuf_copy(nbuf, socket->tftp_pktbuf, nbuf_len); - else - nbuf_len = 0; /* invalid packet */ - netbuf_delete(nbuf); - if (nbuf_len < 4) /* Bad size for a DATA packet */ + if (buf_len < 4) /* Bad size for a DATA packet */ continue; pkt = (struct tftp_packet *)(socket->tftp_pktbuf); @@ -207,7 +166,7 @@ static void tftp_get_packet(struct inode *inode) /* It's the packet we want. We're also EOF if the size < blocksize */ socket->tftp_lastpkt = last_pkt; /* Update last packet number */ - buffersize = nbuf_len - 4; /* Skip TFTP header */ + buffersize = buf_len - 4; /* Skip TFTP header */ socket->tftp_dataptr = socket->tftp_pktbuf + 4; socket->tftp_filepos += buffersize; socket->tftp_bytesleft = buffersize; @@ -243,8 +202,7 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, { struct pxe_pvt_inode *socket = PVT(inode); char *buf; - struct netbuf *nbuf; - u16_t nbuf_len; + uint16_t buf_len; char *p; char *options; char *data; @@ -262,7 +220,8 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, uint16_t opcode; uint16_t blk_num; uint32_t opdata, *opdata_ptr; - struct ip_addr addr; + uint16_t src_port; + uint32_t src_ip; (void)redir; /* TFTP does not redirect */ (void)flags; @@ -279,17 +238,9 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, url->port = TFTP_PORT; socket->ops = &tftp_conn_ops; - socket->conn = netconn_new(NETCONN_UDP); - if (!socket->conn) + if (net_core_open(socket, NET_CORE_UDP)) return; - socket->conn->recv_timeout = 15; /* A 15 ms recv timeout... */ - err = netconn_bind(socket->conn, NULL, 0); - if (err) { - printf("netconn_bind error %d\n", err); - return; - } - buf = rrq_packet_buf; *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ buf += 2; @@ -303,48 +254,42 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, rrq_len = buf - rrq_packet_buf; timeout_ptr = TimeoutTable; /* Reset timeout */ - sendreq: - netconn_disconnect(socket->conn); + net_core_disconnect(socket); timeout = *timeout_ptr++; if (!timeout) return; /* No file available... */ oldtime = jiffies(); - addr.addr = url->ip; - netconn_connect(socket->conn, &addr, url->port); - udp_send(socket->conn, rrq_packet_buf, rrq_len); + net_core_connect(socket, url->ip, url->port); + net_core_send(socket, rrq_packet_buf, rrq_len); /* If the WRITE call fails, we let the timeout take care of it... */ wait_pkt: - netconn_disconnect(socket->conn); + net_core_disconnect(socket); for (;;) { - err = netconn_recv(socket->conn, &nbuf); - if (!nbuf || err) { + buf_len = sizeof(reply_packet_buf); + + err = net_core_recv(socket, reply_packet_buf, &buf_len, + &src_ip, &src_port); + if (err) { jiffies_t now = jiffies(); if (now - oldtime >= timeout) goto sendreq; } else { /* Make sure the packet actually came from the server */ - bool ok_source; - ok_source = netbuf_fromaddr(nbuf)->addr == url->ip; - nbuf_len = netbuf_len(nbuf); - if (nbuf_len <= PKTBUF_SIZE) - netbuf_copy(nbuf, reply_packet_buf, nbuf_len); - else - nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ - netbuf_delete(nbuf); - if (ok_source) - break; + if (src_ip == url->ip) + break; } } - netconn_connect(socket->conn, netbuf_fromaddr(nbuf), netbuf_fromport(nbuf)); + net_core_disconnect(socket); + net_core_connect(socket, src_ip, src_port); /* filesize <- -1 == unknown */ inode->size = -1; socket->tftp_blksize = TFTP_BLOCKSIZE; - buffersize = nbuf_len - 2; /* bytes after opcode */ + buffersize = buf_len - 2; /* bytes after opcode */ if (buffersize < 0) goto wait_pkt; /* Garbled reply */ @@ -491,9 +436,8 @@ err_reply: inode->size = 0; done: - if (!inode->size) { - netconn_delete(socket->conn); - socket->conn = NULL; - } + if (!inode->size) + net_core_close(socket); + return; } diff --git a/core/include/net.h b/core/include/net.h new file mode 100644 index 00000000..4f6819f9 --- /dev/null +++ b/core/include/net.h @@ -0,0 +1,33 @@ +#ifndef _NET_H +#define _NET_H + +#include <stdint.h> +#include <stddef.h> + +/* Protocol family */ +enum net_core_proto { + NET_CORE_TCP, + NET_CORE_UDP, +}; + +void net_core_init(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); + +void net_core_connect(struct pxe_pvt_inode *socket, + uint32_t ip, uint16_t port); +void net_core_disconnect(struct pxe_pvt_inode *socket); + +int net_core_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, + const void *data, size_t len); + +void probe_undi(void); +void pxe_init_isr(void); + +#endif /* _NET_H */ diff --git a/core/legacynet/core.c b/core/legacynet/core.c new file mode 100644 index 00000000..2163c693 --- /dev/null +++ b/core/legacynet/core.c @@ -0,0 +1,186 @@ +#include <syslinux/pxe_api.h> +#include <com32.h> +#include <core.h> +#include <net.h> +#include <pxe.h> +#include <minmax.h> + +/* Common receive buffer */ +static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); + +extern uint16_t get_port(void); +extern void free_port(uint16_t); + +/** + * 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) +{ + struct net_private *priv = &socket->private; + + /* The legacy stack only supports UDP */ + if (proto != NET_CORE_UDP) + return -1; + + /* Allocate local UDP port number */ + priv->tftp_localport = get_port(); + + return 0; +} + +/** + * Close a socket + * + * @param:socket, the socket to open + */ +void net_core_close(struct pxe_pvt_inode *socket) +{ + struct net_private *priv = &socket->private; + + if (priv->tftp_localport) + free_port(priv->tftp_localport); +} + +/** + * 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 net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip, + uint16_t port) +{ + struct net_private *priv = &socket->private; + + socket->tftp_remoteport = htons(port); + priv->tftp_remoteip = ip; + +} + +/** + * Tear down a connection on an open socket + * + * @param:socket, the open socket + */ +void net_core_disconnect(struct pxe_pvt_inode *socket __unused) +{ +} + +/** + * 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 net_core_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; + struct net_private *priv = &socket->private; + uint16_t bytes; + int err; + + udp_read.status = 0; + udp_read.buffer = FAR_PTR(packet_buf); + udp_read.buffer_size = PKTBUF_SIZE; + udp_read.dest_ip = IPInfo.myip; + udp_read.d_port = priv->tftp_localport; + + err = pxe_call(PXENV_UDP_READ, &udp_read); + if (err) + return err; + + if (udp_read.status) + return udp_read.status; + + bytes = min(udp_read.buffer_size, *buf_len); + memcpy(buf, packet_buf, bytes); + + *src_ip = udp_read.src_ip; + *src_port = ntohs(udp_read.s_port); + *buf_len = bytes; + + return 0; +} + +/** + * Send a UDP packet. + * + * @param:socket, the open socket + * @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) +{ + static __lowmem struct s_PXENV_UDP_WRITE udp_write; + struct net_private *priv = &socket->private; + void *lbuf; + uint16_t tid; + + lbuf = lmalloc(len); + if (!lbuf) + return; + + memcpy(lbuf, data, len); + + tid = priv->tftp_localport; /* TID(local port No) */ + udp_write.buffer = FAR_PTR(lbuf); + udp_write.ip = priv->tftp_remoteip; + udp_write.gw = gateway(udp_write.ip); + udp_write.src_port = tid; + udp_write.dst_port = socket->tftp_remoteport; + udp_write.buffer_size = len; + + pxe_call(PXENV_UDP_WRITE, &udp_write); + + lfree(lbuf); +} + +/** + * Network stack-specific initialization + * + * Initialize UDP stack + */ +void net_core_init(void) +{ + int err; + static __lowmem struct s_PXENV_UDP_OPEN udp_open; + udp_open.src_ip = IPInfo.myip; + err = pxe_call(PXENV_UDP_OPEN, &udp_open); + if (err || udp_open.status) { + printf("Failed to initialize UDP stack "); + printf("%d\n", udp_open.status); + kaboom(); + } +} + +void probe_undi(void) +{ +} + +void pxe_init_isr(void) +{ +} + +int reset_pxe(void) +{ + static __lowmem struct s_PXENV_UDP_CLOSE udp_close; + int err = 0; + + pxe_idle_cleanup(); + + pxe_call(PXENV_UDP_CLOSE, &udp_close); + + return err; +} diff --git a/core/legacynet/dnsresolv.c b/core/legacynet/dnsresolv.c new file mode 100644 index 00000000..fab36aef --- /dev/null +++ b/core/legacynet/dnsresolv.c @@ -0,0 +1,353 @@ +#include <stdio.h> +#include <string.h> +#include <core.h> +#include "pxe.h" + +/* DNS CLASS values we care about */ +#define CLASS_IN 1 + +/* DNS TYPE values we care about */ +#define TYPE_A 1 +#define TYPE_CNAME 5 + +/* + * The DNS header structure + */ +struct dnshdr { + uint16_t id; + uint16_t flags; + /* number of entries in the question section */ + uint16_t qdcount; + /* number of resource records in the answer section */ + uint16_t ancount; + /* number of name server resource records in the authority records section*/ + uint16_t nscount; + /* number of resource records in the additional records section */ + uint16_t arcount; +} __attribute__ ((packed)); + +/* + * The DNS query structure + */ +struct dnsquery { + uint16_t qtype; + uint16_t qclass; +} __attribute__ ((packed)); + +/* + * The DNS Resource recodes structure + */ +struct dnsrr { + uint16_t type; + uint16_t class; + uint32_t ttl; + uint16_t rdlength; /* The lenght of this rr data */ + char rdata[]; +} __attribute__ ((packed)); + + +#define DNS_PORT htons(53) /* Default DNS port */ +#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */ + +uint32_t dns_server[DNS_MAX_SERVERS] = {0, }; + + +/* + * Turn a string in _src_ into a DNS "label set" in _dst_; returns the + * number of dots encountered. On return, *dst is updated. + */ +int dns_mangle(char **dst, const char *p) +{ + char *q = *dst; + char *count_ptr; + char c; + int dots = 0; + + count_ptr = q; + *q++ = 0; + + while (1) { + c = *p++; + if (c == 0 || c == ':' || c == '/') + break; + if (c == '.') { + dots++; + count_ptr = q; + *q++ = 0; + continue; + } + + *count_ptr += 1; + *q++ = c; + } + + if (*count_ptr) + *q++ = 0; + + /* update the strings */ + *dst = q; + return dots; +} + + +/* + * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_ + * is allowed pointers relative to a packet in buf. + * + */ +static bool dns_compare(const void *s1, const void *s2, const void *buf) +{ + const uint8_t *q = s1; + const uint8_t *p = s2; + unsigned int c0, c1; + + while (1) { + c0 = p[0]; + if (c0 >= 0xc0) { + /* Follow pointer */ + c1 = p[1]; + p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1; + } else if (c0) { + c0++; /* Include the length byte */ + if (memcmp(q, p, c0)) + return false; + q += c0; + p += c0; + } else { + return *q == 0; + } + } +} + +/* + * Copy a DNS label into a buffer, considering the possibility that we might + * have to follow pointers relative to "buf". + * Returns a pointer to the first free byte *after* the terminal null. + */ +static void *dns_copylabel(void *dst, const void *src, const void *buf) +{ + uint8_t *q = dst; + const uint8_t *p = src; + unsigned int c0, c1; + + while (1) { + c0 = p[0]; + if (c0 >= 0xc0) { + /* Follow pointer */ + c1 = p[1]; + p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1; + } else if (c0) { + c0++; /* Include the length byte */ + memcpy(q, p, c0); + p += c0; + q += c0; + } else { + *q++ = 0; + return q; + } + } +} + +/* + * Skip past a DNS label set in DS:SI + */ +static char *dns_skiplabel(char *label) +{ + uint8_t c; + + while (1) { + c = *label++; + if (c >= 0xc0) + return ++label; /* pointer is two bytes */ + if (c == 0) + return label; + label += c; + } +} + +extern const uint8_t TimeoutTable[]; +extern uint16_t get_port(void); +extern void free_port(uint16_t port); + +/* + * Actual resolver function + * Points to a null-terminated or :-terminated string in _name_ + * and returns the ip addr in _ip_ if it exists and can be found. + * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated + * + * XXX: probably need some caching here. + */ +__export uint32_t dns_resolv(const char *name) +{ + static char __lowmem DNSSendBuf[PKTBUF_SIZE]; + static char __lowmem DNSRecvBuf[PKTBUF_SIZE]; + char *p; + int err; + int dots; + int same; + int rd_len; + int ques, reps; /* number of questions and replies */ + uint8_t timeout; + const uint8_t *timeout_ptr = TimeoutTable; + uint32_t oldtime; + uint32_t srv; + uint32_t *srv_ptr; + struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf; + struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf; + struct dnsquery *query; + struct dnsrr *rr; + static __lowmem struct s_PXENV_UDP_WRITE udp_write; + static __lowmem struct s_PXENV_UDP_READ udp_read; + uint16_t local_port; + uint32_t result = 0; + + /* + * 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; + + /* Make sure we have at least one valid DNS server */ + if (!dns_server[0]) + return 0; + + /* Get a local port number */ + local_port = get_port(); + + /* First, fill the DNS header struct */ + hd1->id++; /* New query ID */ + hd1->flags = htons(0x0100); /* Recursion requested */ + hd1->qdcount = htons(1); /* One question */ + hd1->ancount = 0; /* No answers */ + hd1->nscount = 0; /* No NS */ + hd1->arcount = 0; /* No AR */ + + p = DNSSendBuf + sizeof(struct dnshdr); + dots = dns_mangle(&p, name); /* store the CNAME */ + + if (!dots) { + p--; /* Remove final null */ + /* Uncompressed DNS label set so it ends in null */ + p = stpcpy(p, LocalDomain); + } + + /* Fill the DNS query packet */ + query = (struct dnsquery *)p; + query->qtype = htons(TYPE_A); + query->qclass = htons(CLASS_IN); + p += sizeof(struct dnsquery); + + /* Now send it to name server */ + timeout_ptr = TimeoutTable; + timeout = *timeout_ptr++; + srv_ptr = dns_server; + while (timeout) { + srv = *srv_ptr++; + if (!srv) { + srv_ptr = dns_server; + srv = *srv_ptr++; + } + + udp_write.status = 0; + udp_write.ip = srv; + udp_write.gw = gateway(srv); + udp_write.src_port = local_port; + udp_write.dst_port = DNS_PORT; + udp_write.buffer_size = p - DNSSendBuf; + udp_write.buffer = FAR_PTR(DNSSendBuf); + err = pxe_call(PXENV_UDP_WRITE, &udp_write); + if (err || udp_write.status) + continue; + + oldtime = jiffies(); + do { + if (jiffies() - oldtime >= timeout) + goto again; + + udp_read.status = 0; + udp_read.src_ip = srv; + udp_read.dest_ip = IPInfo.myip; + udp_read.s_port = DNS_PORT; + udp_read.d_port = local_port; + udp_read.buffer_size = PKTBUF_SIZE; + udp_read.buffer = FAR_PTR(DNSRecvBuf); + err = pxe_call(PXENV_UDP_READ, &udp_read); + } while (err || udp_read.status || hd2->id != hd1->id); + + if ((hd2->flags ^ 0x80) & htons(0xf80f)) + goto badness; + + ques = htons(hd2->qdcount); /* Questions */ + reps = htons(hd2->ancount); /* Replies */ + p = DNSRecvBuf + sizeof(struct dnshdr); + while (ques--) { + p = dns_skiplabel(p); /* Skip name */ + p += 4; /* Skip question trailer */ + } + + /* Parse the replies */ + while (reps--) { + same = dns_compare(DNSSendBuf + sizeof(struct dnshdr), + p, DNSRecvBuf); + p = dns_skiplabel(p); + rr = (struct dnsrr *)p; + rd_len = ntohs(rr->rdlength); + if (same && ntohs(rr->class) == CLASS_IN) { + switch (ntohs(rr->type)) { + case TYPE_A: + if (rd_len == 4) { + result = *(uint32_t *)rr->rdata; + goto done; + } + break; + case TYPE_CNAME: + dns_copylabel(DNSSendBuf + sizeof(struct dnshdr), + rr->rdata, DNSRecvBuf); + /* + * We should probably rescan the packet from the top + * here, and technically we might have to send a whole + * new request here... + */ + break; + default: + break; + } + } + + /* not the one we want, try next */ + p += sizeof(struct dnsrr) + rd_len; + } + + badness: + /* + * + ; We got back no data from this server. + ; Unfortunately, for a recursive, non-authoritative + ; query there is no such thing as an NXDOMAIN reply, + ; which technically means we can't draw any + ; conclusions. However, in practice that means the + ; domain doesn't exist. If this turns out to be a + ; problem, we may want to add code to go through all + ; the servers before giving up. + + ; If the DNS server wasn't capable of recursion, and + ; isn't capable of giving us an authoritative reply + ; (i.e. neither AA or RA set), then at least try a + ; different setver... + */ + if (hd2->flags == htons(0x480)) + continue; + + break; /* failed */ + + again: + continue; + } + +done: + free_port(local_port); /* Return port number to the free pool */ + + return result; +} diff --git a/core/legacynet/idle.c b/core/legacynet/idle.c new file mode 100644 index 00000000..e0892379 --- /dev/null +++ b/core/legacynet/idle.c @@ -0,0 +1,112 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include <stdio.h> +#include <string.h> +#include <core.h> +#include <fs.h> +#include <minmax.h> +#include <sys/cpu.h> +#include "pxe.h" + +static int pxe_idle_poll(void) +{ + static __lowmem char junk_pkt[PKTBUF_SIZE]; + static __lowmem t_PXENV_UDP_READ read_buf; + + memset(&read_buf, 0, sizeof read_buf); + + read_buf.src_ip = 0; /* Any destination */ + read_buf.dest_ip = IPInfo.myip; + read_buf.s_port = 0; /* Any source port */ + read_buf.d_port = htons(9); /* Discard port (not used...) */ + read_buf.buffer_size = sizeof junk_pkt; + read_buf.buffer = FAR_PTR(junk_pkt); + + pxe_call(PXENV_UDP_READ, &read_buf); + + return 0; +} + +static uint32_t pxe_detect_nic_type(void) +{ + static __lowmem t_PXENV_UNDI_GET_NIC_TYPE nic_type; + + if (pxe_call(PXENV_UNDI_GET_NIC_TYPE, &nic_type)) + return -1; /* Unknown NIC */ + + if (nic_type.NicType != PCI_NIC && nic_type.NicType != CardBus_NIC) + return -1; /* Not a PCI NIC */ + + /* + * Return VID:DID as a single number, with the VID in the high word + * -- this is opposite from the usual order, but it makes it easier to + * enforce that the table is sorted. + */ + return (nic_type.info.pci.Vendor_ID << 16) + nic_type.info.pci.Dev_ID; +} + +#define PCI_DEV(vid, did) (((vid) << 16) + (did)) + +/* This array should be sorted!! */ +static const uint32_t pxe_need_idle_drain[] = +{ + /* + * Older Broadcom NICs: they need receive calls on idle to avoid + * FIFO stalls. + */ + PCI_DEV(0x14e4, 0x1659), /* BCM5721 */ + PCI_DEV(0x14e4, 0x165a), /* BCM5722 */ + PCI_DEV(0x14e4, 0x165b), /* BCM5723 */ + PCI_DEV(0x14e4, 0x1668), /* BCM5714 */ + PCI_DEV(0x14e4, 0x1669), /* BCM5714S */ + PCI_DEV(0x14e4, 0x166a), /* BCM5780 */ + PCI_DEV(0x14e4, 0x1673), /* BCM5755M */ + PCI_DEV(0x14e4, 0x1674), /* BCM5756ME */ + PCI_DEV(0x14e4, 0x1678), /* BCM5715 */ + PCI_DEV(0x14e4, 0x1679), /* BCM5715S */ + PCI_DEV(0x14e4, 0x167b), /* BCM5755 */ +}; + +void pxe_idle_init(void) +{ + uint32_t dev_id = pxe_detect_nic_type(); + int l, h; + bool found; + + l = 0; + h = sizeof pxe_need_idle_drain / sizeof pxe_need_idle_drain[0] - 1; + + found = false; + while (h >= l) { + int x = (l+h) >> 1; + uint32_t id = pxe_need_idle_drain[x]; + + if (id == dev_id) { + found = true; + break; + } else if (id < dev_id) { + l = x+1; + } else { + h = x-1; + } + } + + if (found) + idle_hook_func = pxe_idle_poll; +} + +void pxe_idle_cleanup(void) +{ + idle_hook_func = NULL; +} diff --git a/core/legacynet/portnum.c b/core/legacynet/portnum.c new file mode 100644 index 00000000..e10af29e --- /dev/null +++ b/core/legacynet/portnum.c @@ -0,0 +1,68 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010 Intel Corporation; author: H. Peter Anvin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include <stdint.h> +#include <stdbool.h> +#include <netinet/in.h> +#include "pxe.h" + +/* Port number bitmap - port numbers 49152 (0xc000) to 57343 (0xefff) */ +#define PORT_NUMBER_BASE 49152 +#define PORT_NUMBER_COUNT 8192 /* Power of 2, please */ +static uint32_t port_number_bitmap[PORT_NUMBER_COUNT/32]; +static uint16_t first_port_number /* = 0 */; + +/* + * Bitmap functions + */ +static bool test_bit(const uint32_t *bitmap, int32_t index) +{ + uint8_t st; + asm("btl %2,%1 ; setc %0" : "=qm" (st) : "m" (*bitmap), "r" (index)); + return st; +} + +static void set_bit(uint32_t *bitmap, int32_t index) +{ + asm volatile("btsl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory"); +} + +static void clr_bit(uint32_t *bitmap, int32_t index) +{ + asm volatile("btcl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory"); +} + +/* + * Get and free a port number (host byte order) + */ +uint16_t get_port(void) +{ + uint16_t port; + + do { + port = first_port_number++; + first_port_number &= PORT_NUMBER_COUNT - 1; + } while (test_bit(port_number_bitmap, port)); + + set_bit(port_number_bitmap, port); + return htons(port + PORT_NUMBER_BASE); +} + +void free_port(uint16_t port) +{ + port = ntohs(port) - PORT_NUMBER_BASE; + + if (port >= PORT_NUMBER_COUNT) + return; + + clr_bit(port_number_bitmap, port); +} diff --git a/core/pxelinux.asm b/core/pxelinux.asm index 7b981a36..d3215e72 100644 --- a/core/pxelinux.asm +++ b/core/pxelinux.asm @@ -525,7 +525,9 @@ pxe_file_exit_hook: ; PXE modules ; ----------------------------------------------------------------------------- +%if IS_LPXELINUX %include "pxeisr.inc" +%endif ; ----------------------------------------------------------------------------- ; Common modules |