summaryrefslogtreecommitdiff
path: root/core/fs
diff options
context:
space:
mode:
Diffstat (limited to 'core/fs')
-rw-r--r--core/fs/chdir.c5
-rw-r--r--core/fs/diskio.c9
-rw-r--r--core/fs/fs.c12
-rw-r--r--core/fs/lib/searchconfig.c3
-rw-r--r--core/fs/pxe/core.c207
-rw-r--r--core/fs/pxe/dhcp_option.c10
-rw-r--r--core/fs/pxe/dnsresolv.c321
-rw-r--r--core/fs/pxe/ftp.c280
-rw-r--r--core/fs/pxe/ftp_readdir.c141
-rw-r--r--core/fs/pxe/gpxeurl.c88
-rw-r--r--core/fs/pxe/http.c400
-rw-r--r--core/fs/pxe/http_readdir.c471
-rw-r--r--core/fs/pxe/idle.c83
-rw-r--r--core/fs/pxe/isr.c278
-rw-r--r--core/fs/pxe/portnum.c68
-rw-r--r--core/fs/pxe/pxe.c1027
-rw-r--r--core/fs/pxe/pxe.h158
-rw-r--r--core/fs/pxe/tcp.c78
-rw-r--r--core/fs/pxe/tftp.c443
-rw-r--r--core/fs/pxe/tftp.h54
-rw-r--r--core/fs/pxe/url.h33
-rw-r--r--core/fs/pxe/urlparse.c223
-rw-r--r--core/fs/readdir.c3
23 files changed, 3037 insertions, 1358 deletions
diff --git a/core/fs/chdir.c b/core/fs/chdir.c
index 5d3a545f..276ea11c 100644
--- a/core/fs/chdir.c
+++ b/core/fs/chdir.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <string.h>
#include <dprintf.h>
+#include <fcntl.h>
#include "fs.h"
#include "cache.h"
@@ -65,7 +66,7 @@ __export size_t realpath(char *dst, const char *src, size_t bufsize)
if (this_fs->fs_ops->realpath) {
s = this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
} else {
- rv = searchdir(src);
+ rv = searchdir(src, O_RDONLY);
if (rv < 0) {
dprintf("realpath: searchpath failure\n");
return -1;
@@ -97,7 +98,7 @@ __export int chdir(const char *src)
return this_fs->fs_ops->chdir(this_fs, src);
/* Otherwise it is a "conventional filesystem" */
- rv = searchdir(src);
+ rv = searchdir(src, O_RDONLY|O_DIRECTORY);
if (rv < 0)
return rv;
diff --git a/core/fs/diskio.c b/core/fs/diskio.c
index 66838161..60defd3e 100644
--- a/core/fs/diskio.c
+++ b/core/fs/diskio.c
@@ -7,6 +7,7 @@
#include <fs.h>
#include <disk.h>
#include <ilog2.h>
+#include <minmax.h>
#define RETRY_COUNT 6
@@ -396,24 +397,20 @@ struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start,
return &disk;
}
-
/*
* Initialize the device structure.
- *
- * NOTE: the disk cache needs to be revamped to support multiple devices...
*/
struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
uint16_t bsHeads, uint16_t bsSecPerTrack,
uint32_t MaxTransfer)
{
static struct device dev;
- static __hugebss char diskcache[128*1024];
dev.disk = disk_init(devno, cdrom, part_start,
bsHeads, bsSecPerTrack, MaxTransfer);
- dev.cache_data = diskcache;
- dev.cache_size = sizeof diskcache;
+ dev.cache_size = 128*1024;
+ dev.cache_data = malloc(dev.cache_size);
return &dev;
}
diff --git a/core/fs/fs.c b/core/fs/fs.c
index 2c10fe95..1cb4b00a 100644
--- a/core/fs/fs.c
+++ b/core/fs/fs.c
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <dprintf.h>
#include "core.h"
#include "dev.h"
@@ -138,7 +139,7 @@ size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
return bytes_read;
}
-int searchdir(const char *name)
+int searchdir(const char *name, int flags)
{
static char root_name[] = "/";
struct file *file;
@@ -155,7 +156,7 @@ int searchdir(const char *name)
/* if we have ->searchdir method, call it */
if (file->fs->fs_ops->searchdir) {
- file->fs->fs_ops->searchdir(name, file);
+ file->fs->fs_ops->searchdir(name, flags, file);
if (file->inode)
return file_to_handle(file);
@@ -336,7 +337,7 @@ err_no_close:
return -1;
}
-__export int open_file(const char *name, struct com32_filedata *filedata)
+__export int open_file(const char *name, int flags, struct com32_filedata *filedata)
{
int rv;
struct file *file;
@@ -345,7 +346,7 @@ __export int open_file(const char *name, struct com32_filedata *filedata)
dprintf("open_file %s\n", name);
mangle_name(mangled_name, name);
- rv = searchdir(mangled_name);
+ rv = searchdir(mangled_name, flags);
if (rv < 0)
return rv;
@@ -399,9 +400,6 @@ void fs_init(com32sys_t *regs)
/* ops is a ptr list for several fs_ops */
const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
- /* Initialize malloc() */
- mem_init();
-
/* Default name for the root directory */
fs.cwd_name[0] = '/';
diff --git a/core/fs/lib/searchconfig.c b/core/fs/lib/searchconfig.c
index b2a964e8..bb1dabf9 100644
--- a/core/fs/lib/searchconfig.c
+++ b/core/fs/lib/searchconfig.c
@@ -1,4 +1,5 @@
#include <dprintf.h>
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <core.h>
@@ -30,7 +31,7 @@ int search_dirs(struct com32_filedata *filedata,
if (realpath(realname, namebuf, FILENAME_MAX) == (size_t)-1)
continue;
dprintf("Config search: %s\n", realname);
- if (open_file(realname, filedata) >= 0) {
+ if (open_file(realname, O_RDONLY, filedata) >= 0) {
chdir(sd);
return 0; /* Got it */
}
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/dhcp_option.c b/core/fs/pxe/dhcp_option.c
index f63d4a91..75827ff7 100644
--- a/core/fs/pxe/dhcp_option.c
+++ b/core/fs/pxe/dhcp_option.c
@@ -2,6 +2,7 @@
#include <string.h>
#include <core.h>
#include <sys/cpu.h>
+#include <lwip/opt.h> /* DNS_MAX_SERVERS */
#include "pxe.h"
char LocalDomain[256];
@@ -48,13 +49,8 @@ static void dns_servers(const void *data, int opt_len)
static void local_domain(const void *data, int opt_len)
{
- char buffer[256];
- char *ld = LocalDomain;
-
- memcpy(buffer, data, opt_len);
- buffer[opt_len] = 0;
-
- dns_mangle(&ld, buffer);
+ memcpy(LocalDomain, data, opt_len);
+ LocalDomain[opt_len] = 0;
}
static void vendor_encaps(const void *data, int opt_len)
diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c
index 184bacbd..afb9e219 100644
--- a/core/fs/pxe/dnsresolv.c
+++ b/core/fs/pxe/dnsresolv.c
@@ -2,6 +2,8 @@
#include <string.h>
#include <core.h>
#include "pxe.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
/* DNS CLASS values we care about */
#define CLASS_IN 1
@@ -48,295 +50,82 @@ struct dnsrr {
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.
+ * parse the ip_str and return the ip address with *res.
+ * return true if the whole string was consumed and the result
+ * was valid.
*
*/
-static bool dns_compare(const void *s1, const void *s2, const void *buf)
+static bool parse_dotquad(const char *ip_str, uint32_t *res)
{
- 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;
+ const char *p = ip_str;
+ uint8_t part = 0;
+ uint32_t ip = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ while (is_digit(*p)) {
+ part = part * 10 + *p - '0';
+ p++;
+ }
+ if (i != 3 && *p != '.')
+ return false;
- 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;
- }
+ ip = (ip << 8) | part;
+ part = 0;
+ p++;
}
-}
-
-/*
- * Skip past a DNS label set in DS:SI
- */
-static char *dns_skiplabel(char *label)
-{
- uint8_t c;
+ p--;
- while (1) {
- c = *label++;
- if (c >= 0xc0)
- return ++label; /* pointer is two bytes */
- if (c == 0)
- return label;
- label += c;
- }
+ *res = htonl(ip);
+ return *p == '\0';
}
/*
- * 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
+ * Actual resolver function.
*
- * XXX: probably need some caching here.
+ * Points to a null-terminated 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
*/
__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;
-
- /* Make sure we have at least one valid DNS server */
- if (!dns_server[0])
+ err_t err;
+ struct ip_addr ip;
+ char fullname[512];
+
+ /*
+ * 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;
- /* 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 it is a valid dot quad, just return that value */
+ if (parse_dotquad(name, &ip.addr))
+ return ip.addr;
- 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 */
+ /* Make sure we have at least one valid DNS server */
+ if (!dns_getserver(0).addr)
+ return 0;
- again:
- continue;
+ /* Is it a local (unqualified) domain name? */
+ if (!strchr(name, '.') && LocalDomain[0]) {
+ snprintf(fullname, sizeof fullname, "%s.%s", name, LocalDomain);
+ name = fullname;
}
-done:
- free_port(local_port); /* Return port number to the free pool */
+ err = netconn_gethostbyname(name, &ip);
+ if (err)
+ return 0;
- return result;
+ return ip.addr;
}
+/*
+ * the one should be called from ASM file
+ */
void pm_pxe_dns_resolv(com32sys_t *regs)
{
const char *name = MK_PTR(regs->ds, regs->esi.w[0]);
diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c
new file mode 100644
index 00000000..5d5a6f03
--- /dev/null
+++ b/core/fs/pxe/ftp.c
@@ -0,0 +1,280 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2009-2011 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ftp.c
+ */
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include <netinet/in.h>
+#include <lwip/api.h>
+#include "core.h"
+#include "fs.h"
+#include "pxe.h"
+#include "thread.h"
+#include "url.h"
+
+static int ftp_cmd_response(struct inode *inode, const char *cmd,
+ const char *cmd_arg,
+ uint8_t *pasv_data, int *pn_ptr)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int c;
+ int pos, code;
+ int pb, pn;
+ bool ps;
+ bool first_line, done;
+ err_t err;
+ char cmd_buf[4096];
+ int cmd_len;
+ const char *p;
+ char *q;
+
+ if (cmd) {
+ cmd_len = strlcpy(cmd_buf, cmd, sizeof cmd_buf);
+ if (cmd_len >= sizeof cmd_buf - 3)
+ return -1;
+ q = cmd_buf + cmd_len;
+
+ if (cmd_arg) {
+ p = cmd_arg;
+
+ *q++ = ' ';
+ cmd_len++;
+ while (*p) {
+ if (++cmd_len < sizeof cmd_buf) *q++ = *p;
+ if (*p == '\r')
+ if (++cmd_len < sizeof cmd_buf) *q++ = '\0';
+ p++;
+ }
+
+ if (cmd_len >= sizeof cmd_buf - 2)
+ return -1;
+ }
+
+ *q++ = '\r';
+ *q++ = '\n';
+ cmd_len += 2;
+
+ err = netconn_write(socket->private.conn, cmd_buf, cmd_len, NETCONN_COPY);
+ if (err)
+ return -1;
+ }
+
+ pos = code = pn = pb = 0;
+ ps = false;
+ first_line = true;
+ done = false;
+
+ while ((c = pxe_getc(inode)) >= 0) {
+ if (c == '\n') {
+ if (done) {
+ if (pn) {
+ pn += ps;
+ if (pn_ptr)
+ *pn_ptr = pn;
+ }
+ return code;
+ }
+ pos = code = 0;
+ first_line = false;
+ continue;
+ }
+
+ switch (pos++) {
+ case 0:
+ case 1:
+ case 2:
+ if (c < '0' || c > '9') {
+ if (first_line)
+ return -1;
+ else
+ pos = 4; /* Skip this line */
+ } else {
+ code = (code*10) + (c - '0');
+ }
+ break;
+
+ case 3:
+ pn = pb = 0;
+ ps = false;
+ if (c == ' ')
+ done = true;
+ else if (c == '-')
+ done = false;
+ else if (first_line)
+ return -1;
+ else
+ done = false;
+ break;
+
+ default:
+ if (pasv_data) {
+ if (c >= '0' && c <= '9') {
+ pb = (pb*10) + (c-'0');
+ if (pn < 6)
+ pasv_data[pn] = pb;
+ ps = true;
+ } else if (c == ',') {
+ pn++;
+ pb = 0;
+ ps = false;
+ } else if (pn) {
+ pn += ps;
+ if (pn_ptr)
+ *pn_ptr = pn;
+ pn = pb = 0;
+ ps = false;
+ }
+ }
+ break;
+ }
+ }
+
+ return -1;
+}
+
+static void ftp_free(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->ctl) {
+ tcp_close_file(socket->ctl);
+ free_socket(socket->ctl);
+ socket->ctl = NULL;
+ }
+ tcp_close_file(inode);
+}
+
+static void ftp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ struct pxe_pvt_inode *ctlsock;
+ int resp;
+
+ ctlsock = socket->ctl ? PVT(socket->ctl) : NULL;
+ 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);
+ }
+ }
+ ftp_free(inode);
+}
+
+static const struct pxe_conn_ops ftp_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = ftp_close_file,
+ .readdir = ftp_readdir,
+};
+
+void ftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ 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;
+ err_t err;
+
+ (void)redir; /* FTP does not redirect */
+
+ inode->size = 0;
+
+ if (!url->port)
+ url->port = 21;
+
+ url_unescape(url->path, 0);
+
+ socket->ops = &ftp_conn_ops;
+
+ /* Set up the control connection */
+ socket->ctl = alloc_inode(inode->fs, 0, sizeof(struct pxe_pvt_inode));
+ if (!socket->ctl)
+ return;
+ ctlsock = PVT(socket->ctl);
+ ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */
+ ctlsock->private.conn = netconn_new(NETCONN_TCP);
+ if (!ctlsock->private.conn)
+ goto err_free;
+ addr.addr = url->ip;
+ err = netconn_connect(ctlsock->private.conn, &addr, url->port);
+ if (err)
+ goto err_delete;
+
+ do {
+ resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
+ } while (resp == 120);
+ if (resp != 220)
+ goto err_disconnect;
+
+ if (!url->user)
+ url->user = "anonymous";
+ if (!url->passwd)
+ url->passwd = "syslinux@";
+
+ resp = ftp_cmd_response(socket->ctl, "USER", url->user, NULL, NULL);
+ if (resp != 202 && resp != 230) {
+ if (resp != 331)
+ goto err_disconnect;
+
+ resp = ftp_cmd_response(socket->ctl, "PASS", url->passwd, NULL, NULL);
+ if (resp != 230)
+ goto err_disconnect;
+ }
+
+ if (!(flags & O_DIRECTORY)) {
+ resp = ftp_cmd_response(socket->ctl, "TYPE", "I", NULL, NULL);
+ if (resp != 200)
+ goto err_disconnect;
+ }
+
+ resp = ftp_cmd_response(socket->ctl, "PASV", NULL, pasv_data, &pasv_bytes);
+ if (resp != 227 || pasv_bytes != 6)
+ goto err_disconnect;
+
+ socket->private.conn = netconn_new(NETCONN_TCP);
+ if (!socket->private.conn)
+ goto err_disconnect;
+ err = netconn_connect(socket->private.conn, (struct ip_addr *)&pasv_data[0],
+ ntohs(*(uint16_t *)&pasv_data[4]));
+ if (err)
+ goto err_disconnect;
+
+ resp = ftp_cmd_response(socket->ctl,
+ (flags & O_DIRECTORY) ? "LIST" : "RETR",
+ url->path, NULL, NULL);
+ if (resp != 125 && resp != 150)
+ goto err_disconnect;
+
+ inode->size = -1;
+ return; /* Sucess! */
+
+err_disconnect:
+ 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->private.conn)
+ netconn_delete(ctlsock->private.conn);
+err_free:
+ free_socket(socket->ctl);
+}
diff --git a/core/fs/pxe/ftp_readdir.c b/core/fs/pxe/ftp_readdir.c
new file mode 100644
index 00000000..6b87f77e
--- /dev/null
+++ b/core/fs/pxe/ftp_readdir.c
@@ -0,0 +1,141 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ftp_readdir.c
+ */
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dprintf.h>
+#include "pxe.h"
+
+static int dirtype(char type)
+{
+ switch (type) {
+ case 'f':
+ return DT_FIFO;
+ case 'c':
+ return DT_CHR;
+ case 'd':
+ return DT_DIR;
+ case 'b':
+ return DT_BLK;
+ case '-':
+ case '0' ... '9': /* Some DOS FTP stacks */
+ return DT_REG;
+ case 'l':
+ return DT_LNK;
+ case 's':
+ return DT_SOCK;
+ default:
+ return DT_UNKNOWN;
+ }
+}
+
+int ftp_readdir(struct inode *inode, struct dirent *dirent)
+{
+ char bufs[2][FILENAME_MAX + 1];
+ int nbuf = 0;
+ char *buf = bufs[nbuf];
+ char *p = buf;
+ char *name = NULL;
+ char type;
+ int c;
+ int dt;
+ bool was_cr = false;
+ bool first = true;
+
+ for (;;) {
+ type = 0;
+
+ for (;;) {
+ c = pxe_getc(inode);
+ if (c == -1)
+ return -1; /* Nothing else there */
+
+ if (c == '\r') {
+ was_cr = true;
+ continue;
+ }
+ if (was_cr) {
+ if (c == '\n') {
+ if (!name) {
+ *p = '\0';
+ name = buf;
+ }
+ break; /* End of line */
+ }
+ else if (c == '\0')
+ c = '\r';
+ }
+ was_cr = false;
+
+ if (c == ' ' || c == '\t') {
+ if (!name) {
+ *p = '\0';
+ if (first) {
+ if (p == buf) {
+ /* Name started with whitespace - skip line */
+ name = buf;
+ } else if ((p = strchr(buf, ';'))) {
+ /* VMS/Multinet format */
+ if (p > buf+4 && !memcmp(p-4, ".DIR", 4)) {
+ type = 'd';
+ p -= 4;
+ } else {
+ type = 'f';
+ }
+ *p = '\0';
+ name = buf;
+ } else {
+ type = buf[0];
+ }
+ first = false;
+ } else {
+ /* Not the first word */
+ if ((type >= '0' && type <= '9') &&
+ !strcmp(buf, "<DIR>")) {
+ /* Some DOS FTP servers */
+ type = 'd';
+ } else if (type == 'l' && !strcmp(buf, "->")) {
+ /* The name was the previous word */
+ name = bufs[nbuf ^ 1];
+ }
+ }
+ nbuf ^= 1;
+ p = buf = bufs[nbuf];
+ }
+ } else {
+ if (!name && p < buf + FILENAME_MAX)
+ *p++ = c;
+ }
+ }
+
+ dt = dirtype(type);
+ if (dt != DT_UNKNOWN) {
+ size_t len = strlen(name);
+
+ if (len <= NAME_MAX) {
+ dirent->d_type = dt;
+ dirent->d_ino = 0; /* Not applicable */
+ dirent->d_off = 0; /* Not applicable */
+ dirent->d_reclen = offsetof(struct dirent, d_name) + len+1;
+ memcpy(dirent->d_name, name, len+1);
+ return 0;
+ }
+ }
+
+ /* Otherwise try the next line... */
+ }
+}
diff --git a/core/fs/pxe/gpxeurl.c b/core/fs/pxe/gpxeurl.c
new file mode 100644
index 00000000..6bbae3c2
--- /dev/null
+++ b/core/fs/pxe/gpxeurl.c
@@ -0,0 +1,88 @@
+#include "pxe.h"
+#if GPXE
+
+static void gpxe_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_CLOSE file_close;
+
+ file_close.FileHandle = socket->tftp_remoteport;
+ pxe_call(PXENV_FILE_CLOSE, &file_close);
+}
+
+/**
+ * Get a fresh packet from a gPXE socket
+ * @param: inode -> Inode pointer
+ *
+ */
+static void gpxe_get_packet(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_READ file_read;
+ int err;
+
+ while (1) {
+ file_read.FileHandle = socket->tftp_remoteport;
+ file_read.Buffer = FAR_PTR(packet_buf);
+ file_read.BufferSize = PKTBUF_SIZE;
+ err = pxe_call(PXENV_FILE_READ, &file_read);
+ if (!err) /* successed */
+ break;
+
+ if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
+ kaboom();
+ }
+
+ memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
+
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ socket->tftp_bytesleft = file_read.BufferSize;
+ socket->tftp_filepos += file_read.BufferSize;
+
+ if (socket->tftp_bytesleft == 0)
+ inode->size = socket->tftp_filepos;
+
+ /* if we're done here, close the file */
+ if (inode->size > socket->tftp_filepos)
+ return;
+
+ /* Got EOF, close it */
+ socket->tftp_goteof = 1;
+ gpxe_close_file(inode);
+}
+
+/**
+ * Open a url using gpxe
+ *
+ * @param:inode, the inode to store our state in
+ * @param:url, the url we want to open
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+void gpxe_open(struct inode *inode, const char *url)
+{
+ static __lowmem struct s_PXENV_FILE_OPEN file_open;
+ static char lowurl[2*FILENAME_MAX];
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int err;
+
+ socket->tftp_pktbuf = malloc(PKTBUF_SIZE);
+ if (!socket->tftp_pktbuf)
+ return;
+
+ snprintf(lowurl, sizeof lowurl, "%s", url);
+ file_open.Status = PXENV_STATUS_BAD_FUNC;
+ file_open.FileName = FAR_PTR(lowurl);
+ err = pxe_call(PXENV_FILE_OPEN, &file_open);
+ if (err)
+ return;
+
+ socket->fill_buffer = gpxe_get_packet;
+ socket->close = gpxe_close_file;
+ socket->tftp_remoteport = file_open.FileHandle;
+ inode->size = -1; /* This is not an error */
+}
+
+#endif /* GPXE */
diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c
new file mode 100644
index 00000000..9bf0c92e
--- /dev/null
+++ b/core/fs/pxe/http.c
@@ -0,0 +1,400 @@
+#include <syslinux/sysappend.h>
+#include <ctype.h>
+#include <lwip/api.h>
+#include "pxe.h"
+#include "../../../version.h"
+#include "url.h"
+
+#define HTTP_PORT 80
+
+static bool is_tspecial(int ch)
+{
+ bool tspecial = false;
+ switch(ch) {
+ case '(': case ')': case '<': case '>': case '@':
+ case ',': case ';': case ':': case '\\': case '"':
+ case '/': case '[': case ']': case '?': case '=':
+ case '{': case '}': case ' ': case '\t':
+ tspecial = true;
+ break;
+ }
+ return tspecial;
+}
+
+static bool is_ctl(int ch)
+{
+ return ch < 0x20;
+}
+
+static bool is_token(int ch)
+{
+ /* Can by antying except a ctl character or a tspecial */
+ return !is_ctl(ch) && !is_tspecial(ch);
+}
+
+static bool append_ch(char *str, size_t size, size_t *pos, int ch)
+{
+ bool success = true;
+ if ((*pos + 1) >= size) {
+ *pos = 0;
+ success = false;
+ } else {
+ str[*pos] = ch;
+ str[*pos + 1] = '\0';
+ *pos += 1;
+ }
+ return success;
+}
+
+static size_t cookie_len, header_len;
+static char *cookie_buf, *header_buf;
+
+__export uint32_t SendCookies = -1UL; /* Send all cookies */
+
+static size_t http_do_bake_cookies(char *q)
+{
+ static const char uchexchar[16] = "0123456789ABCDEF";
+ int i;
+ size_t n = 0;
+ const char *p;
+ char c;
+ bool first = true;
+ uint32_t mask = SendCookies;
+
+ for (i = 0; i < SYSAPPEND_MAX; i++) {
+ if ((mask & 1) && (p = sysappend_strings[i])) {
+ if (first) {
+ if (q) {
+ strcpy(q, "Cookie: ");
+ q += 8;
+ }
+ n += 8;
+ first = false;
+ }
+ if (q) {
+ strcpy(q, "_Syslinux_");
+ q += 10;
+ }
+ n += 10;
+ /* Copy string up to and including '=' */
+ do {
+ c = *p++;
+ if (q)
+ *q++ = c;
+ n++;
+ } while (c != '=');
+ while ((c = *p++)) {
+ if (c == ' ') {
+ if (q)
+ *q++ = '+';
+ n++;
+ } else if (is_token(c)) {
+ if (q)
+ *q++ = c;
+ n++;
+ } else {
+ if (q) {
+ *q++ = '%';
+ *q++ = uchexchar[c >> 4];
+ *q++ = uchexchar[c & 15];
+ }
+ n += 3;
+ }
+ }
+ if (q)
+ *q++ = ';';
+ n++;
+ }
+ mask >>= 1;
+ }
+ if (!first) {
+ if (q) {
+ *q++ = '\r';
+ *q++ = '\n';
+ }
+ n += 2;
+ }
+ if (q)
+ *q = '\0';
+
+ return n;
+}
+
+void http_bake_cookies(void)
+{
+ if (cookie_buf)
+ free(cookie_buf);
+
+ cookie_len = http_do_bake_cookies(NULL);
+ cookie_buf = malloc(cookie_len+1);
+ if (!cookie_buf) {
+ cookie_len = 0;
+ return;
+ }
+
+ if (header_buf)
+ free(header_buf);
+
+ header_len = cookie_len + 6*FILENAME_MAX + 256;
+ header_buf = malloc(header_len);
+ if (!header_buf) {
+ header_len = 0;
+ return; /* Uh-oh... */
+ }
+
+ http_do_bake_cookies(cookie_buf);
+}
+
+static const struct pxe_conn_ops http_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = tcp_close_file,
+ .readdir = http_readdir,
+};
+
+void http_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int header_bytes;
+ const char *next;
+ 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,
+ st_skipline,
+ st_fieldfirst,
+ st_fieldname,
+ st_fieldvalue,
+ st_skip_fieldname,
+ 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;
+
+ (void)flags;
+
+ if (!header_buf)
+ return; /* http is broken... */
+
+ /* This is a straightforward TCP connection after headers */
+ socket->ops = &http_conn_ops;
+
+ /* Reset all of the variables */
+ inode->size = content_length = -1;
+
+ /* Start the http connection */
+ socket->private.conn = netconn_new(NETCONN_TCP);
+ if (!socket->private.conn) {
+ printf("netconn_new failed\n");
+ return;
+ }
+
+ addr.addr = url->ip;
+ if (!url->port)
+ url->port = HTTP_PORT;
+
+ err = netconn_connect(socket->private.conn, &addr, url->port);
+ if (err) {
+ printf("netconn_connect error %d\n", err);
+ goto fail;
+ }
+
+ strcpy(header_buf, "GET /");
+ header_bytes = 5;
+ header_bytes += url_escape_unsafe(header_buf+5, url->path,
+ header_len - 5);
+ if (header_bytes >= header_len)
+ goto fail; /* Buffer overflow */
+ header_bytes += snprintf(header_buf + header_bytes,
+ header_len - header_bytes,
+ " HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "User-Agent: Syslinux/" VERSION_STR "\r\n"
+ "Connection: close\r\n"
+ "%s"
+ "\r\n",
+ url->host, cookie_buf ? cookie_buf : "");
+ if (header_bytes >= header_len)
+ goto fail; /* Buffer overflow */
+
+ err = netconn_write(socket->private.conn, header_buf,
+ header_bytes, NETCONN_NOCOPY);
+ if (err) {
+ printf("netconn_write error %d\n", err);
+ goto fail;
+ }
+
+ /* Parse the HTTP header */
+ state = st_httpver;
+ pos = 0;
+ status = 0;
+ response_size = 0;
+ field_value_len = 0;
+ field_name_len = 0;
+
+ while (state != st_eoh) {
+ int ch = pxe_getc(inode);
+ /* Eof before I finish paring the header */
+ if (ch == -1)
+ goto fail;
+#if 0
+ printf("%c", ch);
+#endif
+ response_size++;
+ if (ch == '\r' || ch == '\0')
+ continue;
+ switch (state) {
+ case st_httpver:
+ if (ch == ' ') {
+ state = st_stcode;
+ pos = 0;
+ }
+ break;
+
+ case st_stcode:
+ if (ch < '0' || ch > '9')
+ goto fail;
+ status = (status*10) + (ch - '0');
+ if (++pos == 3)
+ state = st_skipline;
+ break;
+
+ case st_skipline:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ break;
+
+ case st_fieldfirst:
+ if (ch == '\n')
+ state = st_eoh;
+ else if (isspace(ch)) {
+ /* A continuation line */
+ state = st_fieldvalue;
+ goto fieldvalue;
+ }
+ else if (is_token(ch)) {
+ /* Process the previous field before starting on the next one */
+ if (strcasecmp(field_name, "Content-Length") == 0) {
+ next = field_value;
+ /* Skip leading whitespace */
+ while (isspace(*next))
+ next++;
+ content_length = 0;
+ for (;(*next >= '0' && *next <= '9'); next++) {
+ if ((content_length * 10) < content_length)
+ break;
+ content_length = (content_length * 10) + (*next - '0');
+ }
+ /* In the case of overflow or other error ignore
+ * Content-Length.
+ */
+ if (*next)
+ content_length = -1;
+ }
+ else if (strcasecmp(field_name, "Location") == 0) {
+ next = field_value;
+ /* Skip leading whitespace */
+ while (isspace(*next))
+ next++;
+ strlcpy(location, next, sizeof location);
+ }
+ /* Start the field name and field value afress */
+ field_name_len = 1;
+ field_name[0] = ch;
+ field_name[1] = '\0';
+ field_value_len = 0;
+ field_value[0] = '\0';
+ state = st_fieldname;
+ }
+ else /* Bogus try to recover */
+ state = st_skipline;
+ break;
+
+ case st_fieldname:
+ if (ch == ':' ) {
+ state = st_fieldvalue;
+ }
+ else if (is_token(ch)) {
+ if (!append_ch(field_name, sizeof field_name, &field_name_len, ch))
+ state = st_skip_fieldname;
+ }
+ /* Bogus cases try to recover */
+ else if (ch == '\n')
+ state = st_fieldfirst;
+ else
+ state = st_skipline;
+ break;
+
+ case st_fieldvalue:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ else {
+ fieldvalue:
+ if (!append_ch(field_value, sizeof field_value, &field_value_len, ch))
+ state = st_skip_fieldvalue;
+ }
+ break;
+
+ /* For valid fields whose names are longer than I choose to support. */
+ case st_skip_fieldname:
+ if (ch == ':')
+ state = st_skip_fieldvalue;
+ else if (is_token(ch))
+ state = st_skip_fieldname;
+ /* Bogus cases try to recover */
+ else if (ch == '\n')
+ state = st_fieldfirst;
+ else
+ state = st_skipline;
+ break;
+
+ /* For valid fields whose bodies are longer than I choose to support. */
+ case st_skip_fieldvalue:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ break;
+
+ case st_eoh:
+ break; /* Should never happen */
+ }
+ }
+
+ if (state != st_eoh)
+ status = 0;
+
+ switch (status) {
+ case 200:
+ /*
+ * All OK, need to mark header data consumed and set up a file
+ * structure...
+ */
+ /* Treat the remainder of the bytes as data */
+ socket->tftp_filepos -= response_size;
+ break;
+ case 301:
+ case 302:
+ case 303:
+ case 307:
+ /* A redirect */
+ if (!location[0])
+ goto fail;
+ *redir = location;
+ goto fail;
+ default:
+ goto fail;
+ break;
+ }
+ return;
+fail:
+ inode->size = 0;
+ tcp_close_file(inode);
+ return;
+}
diff --git a/core/fs/pxe/http_readdir.c b/core/fs/pxe/http_readdir.c
new file mode 100644
index 00000000..b6e480e7
--- /dev/null
+++ b/core/fs/pxe/http_readdir.c
@@ -0,0 +1,471 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 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 <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dprintf.h>
+#include "pxe.h"
+
+enum http_readdir_state {
+ st_start, /* 0 Initial state */
+ st_open, /* 1 "<" */
+ st_a, /* 2 "<a" */
+ st_attribute, /* 3 "<a " */
+ st_h, /* 4 "<a h" */
+ st_hr, /* 5 */
+ st_hre, /* 6 */
+ st_href, /* 7 */
+ st_hrefeq, /* 8 */
+ st_hrefqu, /* 9 */
+ st_badtag, /* 10 */
+ st_badtagqu, /* 11 */
+ st_badattr, /* 12 */
+ st_badattrqu, /* 13 */
+};
+
+struct machine {
+ char xchar;
+ uint8_t st_xchar;
+ uint8_t st_left; /* < */
+ uint8_t st_right; /* > */
+ uint8_t st_space; /* white */
+ uint8_t st_other; /* anything else */
+};
+
+static const struct machine statemachine[] = {
+ /* xchar st_xchar st_left st_right st_space st_other */
+ { 0, 0, st_open, st_start, st_start, st_start },
+ { 'a', st_a, st_badtag, st_start, st_open, st_badtag },
+ { 0, 0, st_open, st_open, st_attribute, st_badtag },
+ { 'h', st_h, st_open, st_start, st_attribute, st_badattr },
+ { 'r', st_hr, st_open, st_start, st_attribute, st_badattr },
+ { 'e', st_hre, st_open, st_start, st_attribute, st_badattr },
+ { 'f', st_href, st_open, st_start, st_attribute, st_badattr },
+ { '=', st_hrefeq, st_open, st_start, st_attribute, st_badattr },
+ { '\"', st_hrefqu, st_open, st_start, st_attribute, st_hrefeq },
+ { '\"', st_attribute, st_hrefqu, st_hrefqu, st_hrefqu, st_hrefqu },
+ { '\"', st_badtagqu, st_open, st_start, st_badtag, st_badtag },
+ { '\"', st_badtag, st_badtagqu, st_badtagqu, st_badtagqu, st_badtagqu },
+ { '\"', st_badattrqu, st_open, st_start, st_attribute, st_badattr },
+ { '\"', st_attribute, st_badattrqu, st_badattrqu, st_badattrqu, st_badattrqu },
+};
+
+struct html_entity {
+ uint16_t ucs;
+ const char entity[9];
+};
+
+static const struct html_entity entities[] = {
+ { 34, "quot" },
+ { 38, "amp" },
+ { 60, "lt" },
+ { 62, "gt" },
+#ifdef HTTP_ALL_ENTITIES
+ { 160, "nbsp" },
+ { 161, "iexcl" },
+ { 162, "cent" },
+ { 163, "pound" },
+ { 164, "curren" },
+ { 165, "yen" },
+ { 166, "brvbar" },
+ { 167, "sect" },
+ { 168, "uml" },
+ { 169, "copy" },
+ { 170, "ordf" },
+ { 171, "laquo" },
+ { 172, "not" },
+ { 173, "shy" },
+ { 174, "reg" },
+ { 175, "macr" },
+ { 176, "deg" },
+ { 177, "plusmn" },
+ { 178, "sup2" },
+ { 179, "sup3" },
+ { 180, "acute" },
+ { 181, "micro" },
+ { 182, "para" },
+ { 183, "middot" },
+ { 184, "cedil" },
+ { 185, "sup1" },
+ { 186, "ordm" },
+ { 187, "raquo" },
+ { 188, "frac14" },
+ { 189, "frac12" },
+ { 190, "frac34" },
+ { 191, "iquest" },
+ { 192, "Agrave" },
+ { 193, "Aacute" },
+ { 194, "Acirc" },
+ { 195, "Atilde" },
+ { 196, "Auml" },
+ { 197, "Aring" },
+ { 198, "AElig" },
+ { 199, "Ccedil" },
+ { 200, "Egrave" },
+ { 201, "Eacute" },
+ { 202, "Ecirc" },
+ { 203, "Euml" },
+ { 204, "Igrave" },
+ { 205, "Iacute" },
+ { 206, "Icirc" },
+ { 207, "Iuml" },
+ { 208, "ETH" },
+ { 209, "Ntilde" },
+ { 210, "Ograve" },
+ { 211, "Oacute" },
+ { 212, "Ocirc" },
+ { 213, "Otilde" },
+ { 214, "Ouml" },
+ { 215, "times" },
+ { 216, "Oslash" },
+ { 217, "Ugrave" },
+ { 218, "Uacute" },
+ { 219, "Ucirc" },
+ { 220, "Uuml" },
+ { 221, "Yacute" },
+ { 222, "THORN" },
+ { 223, "szlig" },
+ { 224, "agrave" },
+ { 225, "aacute" },
+ { 226, "acirc" },
+ { 227, "atilde" },
+ { 228, "auml" },
+ { 229, "aring" },
+ { 230, "aelig" },
+ { 231, "ccedil" },
+ { 232, "egrave" },
+ { 233, "eacute" },
+ { 234, "ecirc" },
+ { 235, "euml" },
+ { 236, "igrave" },
+ { 237, "iacute" },
+ { 238, "icirc" },
+ { 239, "iuml" },
+ { 240, "eth" },
+ { 241, "ntilde" },
+ { 242, "ograve" },
+ { 243, "oacute" },
+ { 244, "ocirc" },
+ { 245, "otilde" },
+ { 246, "ouml" },
+ { 247, "divide" },
+ { 248, "oslash" },
+ { 249, "ugrave" },
+ { 250, "uacute" },
+ { 251, "ucirc" },
+ { 252, "uuml" },
+ { 253, "yacute" },
+ { 254, "thorn" },
+ { 255, "yuml" },
+ { 338, "OElig" },
+ { 339, "oelig" },
+ { 352, "Scaron" },
+ { 353, "scaron" },
+ { 376, "Yuml" },
+ { 402, "fnof" },
+ { 710, "circ" },
+ { 732, "tilde" },
+ { 913, "Alpha" },
+ { 914, "Beta" },
+ { 915, "Gamma" },
+ { 916, "Delta" },
+ { 917, "Epsilon" },
+ { 918, "Zeta" },
+ { 919, "Eta" },
+ { 920, "Theta" },
+ { 921, "Iota" },
+ { 922, "Kappa" },
+ { 923, "Lambda" },
+ { 924, "Mu" },
+ { 925, "Nu" },
+ { 926, "Xi" },
+ { 927, "Omicron" },
+ { 928, "Pi" },
+ { 929, "Rho" },
+ { 931, "Sigma" },
+ { 932, "Tau" },
+ { 933, "Upsilon" },
+ { 934, "Phi" },
+ { 935, "Chi" },
+ { 936, "Psi" },
+ { 937, "Omega" },
+ { 945, "alpha" },
+ { 946, "beta" },
+ { 947, "gamma" },
+ { 948, "delta" },
+ { 949, "epsilon" },
+ { 950, "zeta" },
+ { 951, "eta" },
+ { 952, "theta" },
+ { 953, "iota" },
+ { 954, "kappa" },
+ { 955, "lambda" },
+ { 956, "mu" },
+ { 957, "nu" },
+ { 958, "xi" },
+ { 959, "omicron" },
+ { 960, "pi" },
+ { 961, "rho" },
+ { 962, "sigmaf" },
+ { 963, "sigma" },
+ { 964, "tau" },
+ { 965, "upsilon" },
+ { 966, "phi" },
+ { 967, "chi" },
+ { 968, "psi" },
+ { 969, "omega" },
+ { 977, "thetasym" },
+ { 978, "upsih" },
+ { 982, "piv" },
+ { 8194, "ensp" },
+ { 8195, "emsp" },
+ { 8201, "thinsp" },
+ { 8204, "zwnj" },
+ { 8205, "zwj" },
+ { 8206, "lrm" },
+ { 8207, "rlm" },
+ { 8211, "ndash" },
+ { 8212, "mdash" },
+ { 8216, "lsquo" },
+ { 8217, "rsquo" },
+ { 8218, "sbquo" },
+ { 8220, "ldquo" },
+ { 8221, "rdquo" },
+ { 8222, "bdquo" },
+ { 8224, "dagger" },
+ { 8225, "Dagger" },
+ { 8226, "bull" },
+ { 8230, "hellip" },
+ { 8240, "permil" },
+ { 8242, "prime" },
+ { 8243, "Prime" },
+ { 8249, "lsaquo" },
+ { 8250, "rsaquo" },
+ { 8254, "oline" },
+ { 8260, "frasl" },
+ { 8364, "euro" },
+ { 8465, "image" },
+ { 8472, "weierp" },
+ { 8476, "real" },
+ { 8482, "trade" },
+ { 8501, "alefsym" },
+ { 8592, "larr" },
+ { 8593, "uarr" },
+ { 8594, "rarr" },
+ { 8595, "darr" },
+ { 8596, "harr" },
+ { 8629, "crarr" },
+ { 8656, "lArr" },
+ { 8657, "uArr" },
+ { 8658, "rArr" },
+ { 8659, "dArr" },
+ { 8660, "hArr" },
+ { 8704, "forall" },
+ { 8706, "part" },
+ { 8707, "exist" },
+ { 8709, "empty" },
+ { 8711, "nabla" },
+ { 8712, "isin" },
+ { 8713, "notin" },
+ { 8715, "ni" },
+ { 8719, "prod" },
+ { 8721, "sum" },
+ { 8722, "minus" },
+ { 8727, "lowast" },
+ { 8730, "radic" },
+ { 8733, "prop" },
+ { 8734, "infin" },
+ { 8736, "ang" },
+ { 8743, "and" },
+ { 8744, "or" },
+ { 8745, "cap" },
+ { 8746, "cup" },
+ { 8747, "int" },
+ { 8756, "there4" },
+ { 8764, "sim" },
+ { 8773, "cong" },
+ { 8776, "asymp" },
+ { 8800, "ne" },
+ { 8801, "equiv" },
+ { 8804, "le" },
+ { 8805, "ge" },
+ { 8834, "sub" },
+ { 8835, "sup" },
+ { 8836, "nsub" },
+ { 8838, "sube" },
+ { 8839, "supe" },
+ { 8853, "oplus" },
+ { 8855, "otimes" },
+ { 8869, "perp" },
+ { 8901, "sdot" },
+ { 8968, "lceil" },
+ { 8969, "rceil" },
+ { 8970, "lfloor" },
+ { 8971, "rfloor" },
+ { 9001, "lang" },
+ { 9002, "rang" },
+ { 9674, "loz" },
+ { 9824, "spades" },
+ { 9827, "clubs" },
+ { 9829, "hearts" },
+ { 9830, "diams" },
+#endif /* HTTP_ALL_ENTITIES */
+ { 0, "" }
+};
+
+struct entity_state {
+ char entity_buf[16];
+ char *ep;
+};
+
+static char *emit(char *p, int c, struct entity_state *st)
+{
+ const struct html_entity *ent;
+ unsigned int ucs;
+
+ if (!st->ep) {
+ if (c == '&') {
+ /* Entity open */
+ st->ep = st->entity_buf;
+ } else {
+ *p++ = c;
+ }
+ } else {
+ if (c == ';') {
+ st->ep = NULL;
+ *p = '\0';
+ if (st->entity_buf[0] == '#') {
+ if ((st->entity_buf[1] | 0x20)== 'x') {
+ ucs = strtoul(st->entity_buf + 2, NULL, 16);
+ } else {
+ ucs = strtoul(st->entity_buf + 1, NULL, 10);
+ }
+ } else {
+ for (ent = entities; ent->ucs; ent++) {
+ if (!strcmp(st->entity_buf, ent->entity))
+ break;
+ }
+ ucs = ent->ucs;
+ }
+ if (ucs < 32 || ucs >= 0x10ffff)
+ return p; /* Bogus */
+ if (ucs >= 0x10000) {
+ *p++ = 0xf0 + (ucs >> 18);
+ *p++ = 0x80 + ((ucs >> 12) & 0x3f);
+ *p++ = 0x80 + ((ucs >> 6) & 0x3f);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else if (ucs >= 0x800) {
+ *p++ = 0xe0 + (ucs >> 12);
+ *p++ = 0x80 + ((ucs >> 6) & 0x3f);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else if (ucs >= 0x80) {
+ *p++ = 0xc0 + (ucs >> 6);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else {
+ *p++ = ucs;
+ }
+ } else if (st->ep < st->entity_buf + sizeof st->entity_buf - 1) {
+ *st->ep++ = c;
+ }
+ }
+ return p;
+}
+
+static const char *http_get_filename(struct inode *inode, char *buf)
+{
+ int c, lc;
+ char *p;
+ const struct machine *sm;
+ struct entity_state es;
+ enum http_readdir_state state = st_start;
+ enum http_readdir_state pstate = st_start;
+
+ memset(&es, 0, sizeof es);
+
+ p = buf;
+ for (;;) {
+ c = pxe_getc(inode);
+ if (c == -1)
+ return NULL;
+
+ lc = tolower(c);
+
+ sm = &statemachine[state];
+
+ if (lc == sm->xchar)
+ state = sm->st_xchar;
+ else if (c == '<')
+ state = sm->st_left;
+ else if (c == '>')
+ state = sm->st_right;
+ else if (isspace(c))
+ state = sm->st_space;
+ else
+ state = sm->st_other;
+
+ if (state == st_hrefeq || state == st_hrefqu) {
+ if (state != pstate)
+ p = buf;
+ else if (p < buf + FILENAME_MAX)
+ p = emit(p, c, &es);
+ pstate = state;
+ } else {
+ if (pstate != st_start)
+ pstate = st_start;
+ if (p != buf && state == st_start) {
+ *p = '\0';
+ return buf;
+ }
+ }
+ }
+}
+
+int http_readdir(struct inode *inode, struct dirent *dirent)
+{
+ char buf[FILENAME_MAX + 6];
+ const char *fn, *sp;
+
+ for (;;) {
+ fn = http_get_filename(inode, buf);
+
+ if (!fn)
+ return -1; /* End of directory */
+
+ /* Ignore entries with http special characters */
+ if (strchr(fn, '#'))
+ continue;
+ if (strchr(fn, '?'))
+ continue;
+
+ /* A slash if present has to be the last character, and not the first */
+ sp = strchr(fn, '/');
+ if (sp) {
+ if (sp == fn || sp[1])
+ continue;
+ } else {
+ sp = strchr(fn, '\0');
+ }
+
+ if (sp > fn + NAME_MAX)
+ continue;
+
+ dirent->d_ino = 0; /* Not applicable */
+ dirent->d_off = 0; /* Not applicable */
+ dirent->d_reclen = offsetof(struct dirent, d_name) + (sp-fn) + 1;
+ dirent->d_type = *sp == '/' ? DT_DIR : DT_REG;
+ memcpy(dirent->d_name, fn, sp-fn);
+ dirent->d_name[sp-fn] = '\0';
+ return 0;
+ }
+}
diff --git a/core/fs/pxe/idle.c b/core/fs/pxe/idle.c
index 52a87c34..1d1bb8bc 100644
--- a/core/fs/pxe/idle.c
+++ b/core/fs/pxe/idle.c
@@ -19,91 +19,8 @@
#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)
diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c
new file mode 100644
index 00000000..069fefd5
--- /dev/null
+++ b/core/fs/pxe/isr.c
@@ -0,0 +1,278 @@
+/*
+ * core/fs/pxe/isr.c
+ *
+ * Stub invoked on return from real mode including from an interrupt.
+ * Interrupts are locked out on entry.
+ */
+
+#include "core.h"
+#include "thread.h"
+#include "pxe.h"
+#include <string.h>
+#include <sys/cpu.h>
+#include <sys/io.h>
+
+extern uint8_t pxe_irq_pending;
+extern volatile uint8_t pxe_need_poll;
+static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0);
+static DECLARE_INIT_SEMAPHORE(pxe_poll_thread_sem, 0);
+static struct thread *pxe_thread, *poll_thread;
+
+/*
+ * Note: this *must* be called with interrupts enabled.
+ */
+static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
+{
+ far_ptr_t *entry;
+ unsigned int vec;
+ uint8_t mask, mymask;
+ uint32_t now;
+ bool ok;
+
+ if (irq < 8)
+ vec = irq + 0x08;
+ else if (irq < 16)
+ vec = (irq - 8) + 0x70;
+ else
+ return false;
+
+ cli();
+
+ if (pxe_need_poll) {
+ sti();
+ return false;
+ }
+
+ entry = (far_ptr_t *)(vec << 2);
+ *old = *entry;
+ entry->ptr = (uint32_t)isr;
+
+ /* Enable this interrupt at the PIC level, just in case... */
+ mymask = ~(1 << (irq & 7));
+ if (irq >= 8) {
+ mask = inb(0x21);
+ mask &= ~(1 << 2); /* Enable cascade */
+ outb(mask, 0x21);
+ mask = inb(0xa1);
+ mask &= mymask;
+ outb(mask, 0xa1);
+ } else {
+ mask = inb(0x21);
+ mask &= mymask;
+ outb(mask, 0x21);
+ }
+
+ sti();
+
+ now = jiffies();
+
+ /* Some time to watch for stuck interrupts */
+ while (jiffies() - now < 4 && (ok = !pxe_need_poll))
+ hlt();
+
+ if (!ok)
+ *entry = *old; /* Restore the old vector */
+
+ printf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec,
+ old->seg, old->offs, entry->seg, entry->offs);
+
+ return ok;
+}
+
+static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
+{
+ far_ptr_t *entry;
+ unsigned int vec;
+ bool rv;
+
+ if (!irq)
+ return true; /* Nothing to uninstall */
+
+ if (irq < 8)
+ vec = irq + 0x08;
+ else if (irq < 16)
+ vec = (irq - 8) + 0x70;
+ else
+ return false;
+
+ cli();
+
+ entry = (far_ptr_t *)(vec << 2);
+
+ if (entry->ptr != (uint32_t)isr) {
+ rv = false;
+ } else {
+ *entry = *old;
+ rv = true;
+ }
+
+ sti();
+ return rv;
+}
+
+static void pxe_poll_wakeups(void)
+{
+ static jiffies_t last_jiffies = 0;
+ jiffies_t now = jiffies();
+
+ if (pxe_need_poll == 1) {
+ /* If we need polling now, activate polling */
+ pxe_need_poll = 3;
+ sem_up(&pxe_poll_thread_sem);
+ }
+
+ if (now != last_jiffies) {
+ last_jiffies = now;
+ __thread_process_timeouts();
+ }
+
+ if (pxe_irq_pending) {
+ pxe_irq_pending = 0;
+ sem_up(&pxe_receive_thread_sem);
+ }
+}
+
+static void pxe_process_irq(void)
+{
+ static __lowmem t_PXENV_UNDI_ISR isr;
+
+ uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */
+ bool done = false;
+
+ while (!done) {
+ memset(&isr, 0, sizeof isr);
+ isr.FuncFlag = func;
+ func = PXENV_UNDI_ISR_IN_GET_NEXT; /* Next time */
+
+ pxe_call(PXENV_UNDI_ISR, &isr);
+
+ switch (isr.FuncFlag) {
+ case PXENV_UNDI_ISR_OUT_DONE:
+ done = true;
+ break;
+
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ /* Transmit complete - nothing for us to do */
+ break;
+
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ undiif_input(&isr);
+ break;
+
+ case PXENV_UNDI_ISR_OUT_BUSY:
+ /* ISR busy, this should not happen */
+ done = true;
+ break;
+
+ default:
+ /* Invalid return code, this should not happen */
+ done = true;
+ break;
+ }
+ }
+}
+
+static void pxe_receive_thread(void *dummy)
+{
+ (void)dummy;
+
+ for (;;) {
+ sem_down(&pxe_receive_thread_sem, 0);
+ pxe_process_irq();
+ }
+}
+
+static bool pxe_isr_poll(void)
+{
+ static __lowmem t_PXENV_UNDI_ISR isr;
+
+ isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
+ pxe_call(PXENV_UNDI_ISR, &isr);
+
+ return isr.FuncFlag == PXENV_UNDI_ISR_OUT_OURS;
+}
+
+static void pxe_poll_thread(void *dummy)
+{
+ (void)dummy;
+
+ /* Block indefinitely unless activated */
+ sem_down(&pxe_poll_thread_sem, 0);
+
+ for (;;) {
+ cli();
+ if (pxe_receive_thread_sem.count < 0 && pxe_isr_poll())
+ sem_up(&pxe_receive_thread_sem);
+ else
+ __schedule();
+ sti();
+ cpu_relax();
+ }
+}
+
+/*
+ * This does preparations and enables the PXE thread
+ */
+void pxe_init_isr(void)
+{
+ start_idle_thread();
+ sched_hook_func = pxe_poll_wakeups;
+ /*
+ * Run the pxe receive thread at elevated priority, since the UNDI
+ * stack is likely to have very limited memory available; therefore to
+ * avoid packet loss we need to move it into memory that we ourselves
+ * manage, as soon as possible.
+ */
+ core_pm_hook = __schedule;
+
+ pxe_thread = start_thread("pxe receive", 16384, -20,
+ pxe_receive_thread, NULL);
+}
+
+/*
+ * Actually start the interrupt routine inside the UNDI stack
+ */
+void pxe_start_isr(void)
+{
+ int irq = pxe_undi_info.IntNumber;
+
+ if (irq == 2)
+ irq = 9; /* IRQ 2 is really IRQ 9 */
+ else if (irq > 15)
+ irq = 0; /* Invalid IRQ */
+
+ pxe_irq_vector = irq;
+
+ if (irq) {
+ if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain))
+ irq = 0; /* Install failed or stuck interrupt */
+ }
+
+ poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY,
+ pxe_poll_thread, NULL);
+
+ if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ))
+ asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
+}
+
+int reset_pxe(void)
+{
+ static __lowmem struct s_PXENV_UNDI_CLOSE undi_close;
+
+ sched_hook_func = NULL;
+ core_pm_hook = core_pm_null_hook;
+ kill_thread(pxe_thread);
+
+ memset(&undi_close, 0, sizeof(undi_close));
+ pxe_call(PXENV_UNDI_CLOSE, &undi_close);
+
+ if (undi_close.Status)
+ printf("PXENV_UNDI_CLOSE failed: 0x%x\n", undi_close.Status);
+
+ if (pxe_irq_vector)
+ uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain);
+ if (poll_thread)
+ kill_thread(poll_thread);
+
+ return undi_close.Status;
+}
diff --git a/core/fs/pxe/portnum.c b/core/fs/pxe/portnum.c
deleted file mode 100644
index 19af0cd0..00000000
--- a/core/fs/pxe/portnum.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * 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/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index c07f00ee..9f18f282 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -5,10 +5,16 @@
#include <bios.h>
#include <fs.h>
#include <minmax.h>
+#include <fcntl.h>
#include <sys/cpu.h>
#include "pxe.h"
+#include "thread.h"
+#include "url.h"
+#include "tftp.h"
+#include <net.h>
-#define GPXE 1
+__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 */
@@ -16,45 +22,26 @@ uint8_t MAC[MAC_MAX]; /* Actual MAC address */
uint8_t MAC_len; /* MAC address len */
uint8_t MAC_type; /* MAC address type */
-char __bss16 BOOTIFStr[7+3*(MAC_MAX+1)];
-#define MAC_str (BOOTIFStr+7) /* The actual hardware address */
-char __bss16 SYSUUIDStr[8+32+5];
-#define UUID_str (SYSUUIDStr+8) /* The actual UUID */
-
char boot_file[256]; /* From DHCP */
char path_prefix[256]; /* From DHCP */
-char dot_quad_buf[16];
static bool has_gpxe;
static uint32_t gpxe_funcs;
bool have_uuid = false;
-/* Common receive buffer */
-static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
-
-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
-};
-
-struct tftp_options {
- const char *str_ptr; /* string pointer */
- size_t offset; /* offset into socket structre */
-};
-
-#define IFIELD(x) offsetof(struct inode, x)
-#define PFIELD(x) (offsetof(struct inode, pvt) + \
- offsetof(struct pxe_pvt_inode, x))
-
-static const struct tftp_options tftp_options[] =
-{
- { "tsize", IFIELD(size) },
- { "blksize", PFIELD(tftp_blksize) },
+static struct url_scheme {
+ const char *name;
+ void (*open)(struct url_info *, int, struct inode *, const char **);
+ 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 },
};
-static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
-
-static void tftp_error(struct inode *file, uint16_t errnum,
- const char *errstr);
+#define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY)
/*
* Allocate a local UDP port structure and assign it a local port number.
@@ -67,108 +54,35 @@ static struct inode *allocate_socket(struct fs_info *fs)
if (!inode) {
malloc_error("socket structure");
} else {
- struct pxe_pvt_inode *socket = PVT(inode);
- socket->tftp_localport = get_port();
inode->mode = DT_REG; /* No other types relevant for PXE */
}
return inode;
}
-static void free_socket(struct inode *inode)
+void free_socket(struct inode *inode)
{
struct pxe_pvt_inode *socket = PVT(inode);
- free_port(socket->tftp_localport);
+ free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */
free_inode(inode);
}
-#if GPXE
-static void gpxe_close_file(struct inode *inode)
-{
- struct pxe_pvt_inode *socket = PVT(inode);
- static __lowmem struct s_PXENV_FILE_CLOSE file_close;
-
- file_close.FileHandle = socket->tftp_remoteport;
- pxe_call(PXENV_FILE_CLOSE, &file_close);
-}
-#endif
-
static void pxe_close_file(struct file *file)
{
struct inode *inode = file->inode;
struct pxe_pvt_inode *socket = PVT(inode);
+ if (!inode)
+ return;
+
if (!socket->tftp_goteof) {
-#if GPXE
- if (socket->tftp_localport == 0xffff) {
- gpxe_close_file(inode);
- } else
-#endif
- if (socket->tftp_localport != 0) {
- tftp_error(inode, 0, "No error, file close");
- }
+ socket->ops->close(inode);
}
free_socket(inode);
}
-/**
- * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
- *
- * @param: dst, output buffer
- * @param: src, input buffer
- * @param: count, number of bytes
- *
- */
-static void lchexbytes(char *dst, const void *src, int count)
-{
- uint8_t half;
- uint8_t c;
- const uint8_t *s = src;
-
- for(; count > 0; count--) {
- c = *s++;
- half = ((c >> 4) & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
-
- half = (c & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
- }
-}
-
-/*
- * just like the lchexbytes, except to upper-case
- *
- */
-static void uchexbytes(char *dst, const void *src, int count)
-{
- uint8_t half;
- uint8_t c;
- const uint8_t *s = src;
-
- for(; count > 0; count--) {
- c = *s++;
- half = ((c >> 4) & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
-
- half = (c & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
- }
-}
-
-/*
- * Parse a single hexadecimal byte, which must be complete (two
- * digits). This is used in URL parsing.
- */
-static int hexbyte(const char *p)
-{
- if (!is_hex(p[0]) || !is_hex(p[1]))
- return -1;
- else
- return (hexval(p[0]) << 4) + hexval(p[1]);
-}
-
/*
* Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
* We used to refuse class E, but class E addresses are likely to become
@@ -197,57 +111,11 @@ bool ip_ok(uint32_t ip)
*/
static int gendotquad(char *dst, uint32_t ip)
{
- int part;
- int i = 0, j;
- char temp[4];
- char *p = dst;
-
- for (; i < 4; i++) {
- j = 0;
- part = ip & 0xff;
- do {
- temp[j++] = (part % 10) + '0';
- }while(part /= 10);
- for (; j > 0; j--)
- *p++ = temp[j-1];
- *p++ = '.';
-
- ip >>= 8;
- }
- /* drop the last dot '.' and zero-terminate string*/
- *(--p) = 0;
-
- return p - dst;
-}
-
-/*
- * parse the ip_str and return the ip address with *res.
- * return the the string address after the ip string
- *
- */
-static const char *parse_dotquad(const char *ip_str, uint32_t *res)
-{
- const char *p = ip_str;
- uint8_t part = 0;
- uint32_t ip = 0;
- int i;
-
- for (i = 0; i < 4; i++) {
- while (is_digit(*p)) {
- part = part * 10 + *p - '0';
- p++;
- }
- if (i != 3 && *p != '.')
- return NULL;
-
- ip = (ip << 8) | part;
- part = 0;
- p++;
- }
- p--;
-
- *res = htonl(ip);
- return p;
+ return 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]);
}
/*
@@ -256,11 +124,14 @@ static const char *parse_dotquad(const char *ip_str, uint32_t *res)
*/
__export int pxe_call(int opcode, void *data)
{
+ static DECLARE_INIT_SEMAPHORE(pxe_sem, 1);
extern void pxenv(void);
com32sys_t regs;
+ sem_down(&pxe_sem, 0);
+
#if 0
- printf("pxe_call op %04x data %p\n", opcode, data);
+ dprintf("pxe_call op %04x data %p\n", opcode, data);
#endif
memset(&regs, 0, sizeof regs);
@@ -269,77 +140,11 @@ __export int pxe_call(int opcode, void *data)
regs.edi.w[0] = OFFS(data);
call16(pxenv, &regs, &regs);
- return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
-}
-
-/**
- * Send an ERROR packet. This is used to terminate a connection.
- *
- * @inode: Inode structure
- * @errnum: Error number (network byte order)
- * @errstr: Error string (included in packet)
- */
-static void tftp_error(struct inode *inode, uint16_t errnum,
- const char *errstr)
-{
- static __lowmem struct {
- uint16_t err_op;
- uint16_t err_num;
- char err_msg[64];
- } __packed err_buf;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
- struct pxe_pvt_inode *socket = PVT(inode);
-
- err_buf.err_op = TFTP_ERROR;
- err_buf.err_num = errnum;
- memcpy(err_buf.err_msg, errstr, len);
- err_buf.err_msg[len] = '\0';
-
- udp_write.src_port = socket->tftp_localport;
- udp_write.dst_port = socket->tftp_remoteport;
- udp_write.ip = socket->tftp_remoteip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.buffer = FAR_PTR(&err_buf);
- udp_write.buffer_size = 4 + len + 1;
+ sem_up(&pxe_sem);
- /* If something goes wrong, there is nothing we can do, anyway... */
- pxe_call(PXENV_UDP_WRITE, &udp_write);
-}
-
-
-/**
- * Send ACK packet. This is a common operation and so is worth canning.
- *
- * @param: inode, Inode pointer
- * @param: ack_num, Packet # to ack (network byte order)
- *
- */
-static void ack_packet(struct inode *inode, uint16_t ack_num)
-{
- int err;
- static __lowmem uint16_t ack_packet_buf[2];
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- struct pxe_pvt_inode *socket = PVT(inode);
-
- /* Packet number to ack */
- ack_packet_buf[0] = TFTP_ACK;
- ack_packet_buf[1] = ack_num;
- udp_write.src_port = socket->tftp_localport;
- udp_write.dst_port = socket->tftp_remoteport;
- udp_write.ip = socket->tftp_remoteip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.buffer = FAR_PTR(ack_packet_buf);
- udp_write.buffer_size = 4;
-
- err = pxe_call(PXENV_UDP_WRITE, &udp_write);
- (void)err;
-#if 0
- printf("sent %s\n", err ? "FAILED" : "OK");
-#endif
+ return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
}
-
/**
* Get a DHCP packet from the PXE stack into a lowmem buffer
*
@@ -353,7 +158,7 @@ static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
printf(" %02x", type);
- get_cached_info.Status = 0;
+ 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);
@@ -366,104 +171,13 @@ static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
return get_cached_info.BufferSize;
}
-
-/*
- * Return the type of pathname passed.
- */
-enum pxe_path_type {
- PXE_RELATIVE, /* No :: or URL */
- PXE_HOMESERVER, /* Starting with :: */
- PXE_TFTP, /* host:: */
- PXE_URL_TFTP, /* tftp:// */
- PXE_URL, /* Absolute URL syntax */
-};
-
-static enum pxe_path_type pxe_path_type(const char *str)
-{
- const char *p;
-
- p = str;
-
- while (1) {
- switch (*p) {
- case ':':
- if (p[1] == ':') {
- if (p == str)
- return PXE_HOMESERVER;
- else
- return PXE_TFTP;
- } else if (p > str && p[1] == '/' && p[2] == '/') {
- if (!strncasecmp(str, "tftp://", 7))
- return PXE_URL_TFTP;
- else
- return PXE_URL;
- }
-
- /* else fall through */
- case '/': case '!': case '@': case '#': case '%':
- case '^': case '&': case '*': case '(': case ')':
- case '[': case ']': case '{': case '}': case '\\':
- case '|': case '=': case '`': case '~': case '\'':
- case '\"': case ';': case '>': case '<': case '?':
- case '\0':
- /* Any of these characters terminate the colon search */
- return PXE_RELATIVE;
- default:
- break;
- }
- p++;
- }
-}
-
-#if GPXE
-
-/**
- * Get a fresh packet from a gPXE socket
- * @param: inode -> Inode pointer
- *
- */
-static void get_packet_gpxe(struct inode *inode)
-{
- struct pxe_pvt_inode *socket = PVT(inode);
- static __lowmem struct s_PXENV_FILE_READ file_read;
- int err;
-
- while (1) {
- file_read.FileHandle = socket->tftp_remoteport;
- file_read.Buffer = FAR_PTR(packet_buf);
- file_read.BufferSize = PKTBUF_SIZE;
- err = pxe_call(PXENV_FILE_READ, &file_read);
- if (!err) /* successed */
- break;
-
- if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
- kaboom();
- }
-
- memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
-
- socket->tftp_dataptr = socket->tftp_pktbuf;
- socket->tftp_bytesleft = file_read.BufferSize;
- socket->tftp_filepos += file_read.BufferSize;
-
- if (socket->tftp_bytesleft == 0)
- inode->size = socket->tftp_filepos;
-
- /* if we're done here, close the file */
- if (inode->size > socket->tftp_filepos)
- return;
-
- /* Got EOF, close it */
- socket->tftp_goteof = 1;
- gpxe_close_file(inode);
-}
-#endif /* GPXE */
-
-
/*
* mangle a filename pointed to by _src_ into a buffer pointed
* to by _dst_; ends on encountering any whitespace.
*
+ * This deliberately does not attempt to do any conversion of
+ * pathname separators.
+ *
*/
static void pxe_mangle_name(char *dst, const char *src)
{
@@ -476,109 +190,40 @@ static void pxe_mangle_name(char *dst, const char *src)
}
/*
- * Get a fresh packet if the buffer is drained, and we haven't hit
- * EOF yet. The buffer should be filled immediately after draining!
+ * Read a single character from the specified pxe inode.
+ * Very useful for stepping through http streams and
+ * parsing their headers.
*/
-static void fill_buffer(struct inode *inode)
+int pxe_getc(struct inode *inode)
{
- int err;
- int last_pkt;
- const uint8_t *timeout_ptr;
- uint8_t timeout;
- uint16_t buffersize;
- uint32_t oldtime;
- void *data = NULL;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
struct pxe_pvt_inode *socket = PVT(inode);
+ unsigned char byte;
- if (socket->tftp_bytesleft || socket->tftp_goteof)
- return;
+ while (!socket->tftp_bytesleft) {
+ if (socket->tftp_goteof)
+ return -1;
-#if GPXE
- if (socket->tftp_localport == 0xffff) {
- get_packet_gpxe(inode);
- return;
+ socket->ops->fill_buffer(inode);
}
-#endif
- /*
- * Start by ACKing the previous packet; this should cause
- * the next packet to be sent.
- */
- timeout_ptr = TimeoutTable;
- timeout = *timeout_ptr++;
- oldtime = jiffies();
-
- ack_again:
- ack_packet(inode, socket->tftp_lastpkt);
-
- while (timeout) {
- udp_read.buffer = FAR_PTR(packet_buf);
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.src_ip = socket->tftp_remoteip;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.s_port = socket->tftp_remoteport;
- udp_read.d_port = socket->tftp_localport;
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- if (err) {
- uint32_t now = jiffies();
-
- if (now-oldtime >= timeout) {
- oldtime = now;
- timeout = *timeout_ptr++;
- if (!timeout)
- break;
- }
- continue;
- }
+ byte = *socket->tftp_dataptr;
+ socket->tftp_bytesleft -= 1;
+ socket->tftp_dataptr += 1;
- if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */
- continue;
-
- data = packet_buf;
- if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
- continue;
-
- /* If goes here, recevie OK, break */
- break;
- }
-
- /* time runs out */
- if (timeout == 0)
- kaboom();
+ return byte;
+}
- last_pkt = socket->tftp_lastpkt;
- last_pkt = ntohs(last_pkt); /* Host byte order */
- last_pkt++;
- last_pkt = htons(last_pkt); /* Network byte order */
- if (*(uint16_t *)(data + 2) != last_pkt) {
- /*
- * Wrong packet, ACK the packet and try again.
- * This is presumably because the ACK got lost,
- * so the server just resent the previous packet.
- */
-#if 0
- printf("Wrong packet, wanted %04x, got %04x\n", \
- htons(last_pkt), htons(*(uint16_t *)(data+2)));
-#endif
- goto ack_again;
- }
+/*
+ * Get a fresh packet if the buffer is drained, and we haven't hit
+ * EOF yet. The buffer should be filled immediately after draining!
+ */
+static void fill_buffer(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ if (socket->tftp_bytesleft || socket->tftp_goteof)
+ return;
- /* It's the packet we want. We're also EOF if the size < blocksize */
- socket->tftp_lastpkt = last_pkt; /* Update last packet number */
- buffersize = udp_read.buffer_size - 4; /* Skip TFTP header */
- memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize);
- socket->tftp_dataptr = socket->tftp_pktbuf;
- socket->tftp_filepos += buffersize;
- socket->tftp_bytesleft = buffersize;
- if (buffersize < socket->tftp_blksize) {
- /* it's the last block, ACK packet immediately */
- ack_packet(inode, *(uint16_t *)(data + 2));
-
- /* Make sure we know we are at end of file */
- inode->size = socket->tftp_filepos;
- socket->tftp_goteof = 1;
- }
+ return socket->ops->fill_buffer(inode);
}
@@ -635,8 +280,20 @@ static uint32_t pxe_getfssec(struct file *file, char *buf,
return bytes_read;
}
+/*
+ * Assign an IP address to a URL
+ */
+static void url_set_ip(struct url_info *url)
+{
+ url->ip = 0;
+ if (url->host)
+ url->ip = dns_resolv(url->host);
+ if (!url->ip)
+ url->ip = IPInfo.serverip;
+}
+
/**
- * Open a TFTP connection to the server
+ * Open the specified connection
*
* @param:filename, the file we wanna open
*
@@ -644,352 +301,95 @@ static uint32_t pxe_getfssec(struct file *file, char *buf,
* @out: the lenght of this file, stores in file->file_len
*
*/
-static void __pxe_searchdir(const char *filename, struct file *file);
+static void __pxe_searchdir(const char *filename, int flags, struct file *file);
extern uint16_t PXERetry;
-static void pxe_searchdir(const char *filename, struct file *file)
+static void pxe_searchdir(const char *filename, int flags, struct file *file)
{
int i = PXERetry;
do {
dprintf("PXE: file = %p, retries left = %d: ", file, i);
- __pxe_searchdir(filename, file);
+ __pxe_searchdir(filename, flags, file);
dprintf("%s\n", file->inode ? "ok" : "failed");
} while (!file->inode && i--);
}
-
-static void __pxe_searchdir(const char *filename, struct file *file)
+static void __pxe_searchdir(const char *filename, int flags, struct file *file)
{
struct fs_info *fs = file->fs;
struct inode *inode;
- struct pxe_pvt_inode *socket;
- char *buf;
- const char *np;
- char *p;
- char *options;
- char *data;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
- static __lowmem struct s_PXENV_FILE_OPEN file_open;
- static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
- static __lowmem char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
- const struct tftp_options *tftp_opt;
- int i = 0;
- int err;
- int buffersize;
- int rrq_len;
- const uint8_t *timeout_ptr;
- uint32_t timeout;
- uint32_t oldtime;
- uint16_t tid;
- uint16_t opcode;
- uint16_t blk_num;
- uint32_t ip = 0;
- uint32_t opdata, *opdata_ptr;
- enum pxe_path_type path_type;
char fullpath[2*FILENAME_MAX];
- uint16_t server_port = TFTP_PORT; /* TFTP server port */
+#if GPXE
+ char urlsave[2*FILENAME_MAX];
+#endif
+ struct url_info url;
+ const struct url_scheme *us = NULL;
+ int redirect_count = 0;
+ bool found_scheme = false;
inode = file->inode = NULL;
-
- buf = rrq_packet_buf;
- *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
- buf += 2;
-
- path_type = pxe_path_type(filename);
- if (path_type == PXE_RELATIVE) {
- snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
- path_type = pxe_path_type(filename = fullpath);
- }
- switch (path_type) {
- case PXE_RELATIVE: /* Really shouldn't happen... */
- case PXE_URL:
- buf = stpcpy(buf, filename);
- ip = IPInfo.serverip; /* Default server */
- break;
-
- case PXE_HOMESERVER:
- buf = stpcpy(buf, filename+2);
- ip = IPInfo.serverip;
- break;
-
- case PXE_TFTP:
- np = strchr(filename, ':');
- buf = stpcpy(buf, np+2);
- if (parse_dotquad(filename, &ip) != np)
- ip = dns_resolv(filename);
- break;
-
- case PXE_URL_TFTP:
- np = filename + 7;
- while (*np && *np != '/' && *np != ':')
- np++;
- if (np > filename + 7) {
- if (parse_dotquad(filename + 7, &ip) != np)
- ip = dns_resolv(filename + 7);
- }
- if (*np == ':') {
- np++;
- server_port = 0;
- while (*np >= '0' && *np <= '9')
- server_port = server_port * 10 + *np++ - '0';
- server_port = server_port ? htons(server_port) : TFTP_PORT;
- }
- if (*np == '/')
- np++; /* Do *NOT* eat more than one slash here... */
- /*
- * The ; is because of a quirk in the TFTP URI spec (RFC
- * 3617); it is to be followed by TFTP modes, which we just ignore.
- */
- while (*np && *np != ';') {
- int v;
- if (*np == '%' && (v = hexbyte(np+1)) > 0) {
- *buf++ = v;
- np += 3;
- } else {
- *buf++ = *np++;
- }
- }
- *buf = '\0';
- break;
- }
-
- buf++; /* Point *past* the final NULL */
- memcpy(buf, rrq_tail, sizeof rrq_tail);
- buf += sizeof rrq_tail;
-
- rrq_len = buf - rrq_packet_buf;
-
- inode = allocate_socket(fs);
- if (!inode)
- return; /* Allocation failure */
- socket = PVT(inode);
+ while (filename) {
+ if (redirect_count++ > 5)
+ break;
+ strlcpy(fullpath, filename, sizeof fullpath);
#if GPXE
- if (path_type == PXE_URL) {
- if (has_gpxe) {
- file_open.Status = PXENV_STATUS_BAD_FUNC;
- file_open.FileName = FAR_PTR(rrq_packet_buf + 2);
- err = pxe_call(PXENV_FILE_OPEN, &file_open);
- if (err)
- goto done;
-
- socket->tftp_localport = -1;
- socket->tftp_remoteport = file_open.FileHandle;
- inode->size = -1;
- goto done;
- } else {
- static bool already = false;
- if (!already) {
- printf("URL syntax, but gPXE extensions not detected, "
- "trying plain TFTP...\n");
- already = true;
- }
+ strcpy(urlsave, fullpath);
+#endif
+ parse_url(&url, fullpath);
+ if (url.type == URL_SUFFIX) {
+ snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
+#if GPXE
+ strcpy(urlsave, fullpath);
+#endif
+ parse_url(&url, fullpath);
}
- }
-#endif /* GPXE */
-
- if (!ip)
- goto done; /* No server */
-
- timeout_ptr = TimeoutTable; /* Reset timeout */
-
-sendreq:
- timeout = *timeout_ptr++;
- if (!timeout) {
- free_socket(inode);
- return; /* No file available... */
- }
- oldtime = jiffies();
-
- socket->tftp_remoteip = ip;
- tid = socket->tftp_localport; /* TID(local port No) */
- udp_write.buffer = FAR_PTR(rrq_packet_buf);
- udp_write.ip = ip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.src_port = tid;
- udp_write.dst_port = server_port;
- udp_write.buffer_size = rrq_len;
- pxe_call(PXENV_UDP_WRITE, &udp_write);
-
- /* If the WRITE call fails, we let the timeout take care of it... */
-
-wait_pkt:
- for (;;) {
- buf = packet_buf;
- udp_read.status = 0;
- udp_read.buffer = FAR_PTR(buf);
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.d_port = tid;
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- if (err || udp_read.status) {
- uint32_t now = jiffies();
- if (now - oldtime >= timeout)
- goto sendreq;
- } else {
- /* Make sure the packet actually came from the server */
- if (udp_read.src_ip == socket->tftp_remoteip)
+
+ inode = allocate_socket(fs);
+ if (!inode)
+ return; /* Allocation failure */
+
+ url_set_ip(&url);
+
+ filename = NULL;
+ found_scheme = false;
+ for (us = url_schemes; us->name; us++) {
+ if (!strcmp(us->name, url.scheme)) {
+ if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0)
+ us->open(&url, flags, inode, &filename);
+ found_scheme = true;
break;
+ }
}
- }
-
- socket->tftp_remoteport = udp_read.s_port;
-
- /* filesize <- -1 == unknown */
- inode->size = -1;
- /* Default blksize unless blksize option negotiated */
- socket->tftp_blksize = TFTP_BLOCKSIZE;
- buffersize = udp_read.buffer_size - 2; /* bytes after opcode */
- if (buffersize < 0)
- goto wait_pkt; /* Garbled reply */
-
- /*
- * Get the opcode type, and parse it
- */
- opcode = *(uint16_t *)packet_buf;
- switch (opcode) {
- case TFTP_ERROR:
- inode->size = 0;
- break; /* ERROR reply; don't try again */
-
- case TFTP_DATA:
- /*
- * If the server doesn't support any options, we'll get a
- * DATA reply instead of OACK. Stash the data in the file
- * buffer and go with the default value for all options...
- *
- * We got a DATA packet, meaning no options are
- * suported. Save the data away and consider the
- * length undefined, *unless* this is the only
- * data packet...
- */
- buffersize -= 2;
- if (buffersize < 0)
- goto wait_pkt;
- data = packet_buf + 2;
- blk_num = *(uint16_t *)data;
- data += 2;
- if (blk_num != htons(1))
- goto wait_pkt;
- socket->tftp_lastpkt = blk_num;
- if (buffersize > TFTP_BLOCKSIZE)
- goto err_reply; /* Corrupt */
- else if (buffersize < TFTP_BLOCKSIZE) {
- /*
- * This is the final EOF packet, already...
- * We know the filesize, but we also want to
- * ack the packet and set the EOF flag.
- */
- inode->size = buffersize;
- socket->tftp_goteof = 1;
- ack_packet(inode, blk_num);
- }
-
- socket->tftp_bytesleft = buffersize;
- socket->tftp_dataptr = socket->tftp_pktbuf;
- memcpy(socket->tftp_pktbuf, data, buffersize);
- break;
-
- case TFTP_OACK:
- /*
- * Now we need to parse the OACK packet to get the transfer
- * and packet sizes.
- */
-
- options = packet_buf + 2;
- p = options;
-
- while (buffersize) {
- const char *opt = p;
- /*
- * If we find an option which starts with a NUL byte,
- * (a null option), we're either seeing garbage that some
- * TFTP servers add to the end of the packet, or we have
- * no clue how to parse the rest of the packet (what is
- * an option name and what is a value?) In either case,
- * discard the rest.
- */
- if (!*opt)
- goto done;
-
- while (buffersize) {
- if (!*p)
- break; /* Found a final null */
- *p++ |= 0x20;
- buffersize--;
- }
- if (!buffersize)
- break; /* Unterminated option */
-
- /* Consume the terminal null */
- p++;
- buffersize--;
-
- if (!buffersize)
- break; /* No option data */
-
- /*
- * Parse option pointed to by options; guaranteed to be
- * null-terminated
- */
- tftp_opt = tftp_options;
- for (i = 0; i < tftp_nopts; i++) {
- if (!strcmp(opt, tftp_opt->str_ptr))
- break;
- tftp_opt++;
- }
- if (i == tftp_nopts)
- goto err_reply; /* Non-negotitated option returned,
- no idea what it means ...*/
-
- /* get the address of the filed that we want to write on */
- opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
- opdata = 0;
-
- /* do convert a number-string to decimal number, just like atoi */
- while (buffersize--) {
- uint8_t d = *p++;
- if (d == '\0')
- break; /* found a final null */
- d -= '0';
- if (d > 9)
- goto err_reply; /* Not a decimal digit */
- opdata = opdata*10 + d;
- }
- *opdata_ptr = opdata;
- }
- break;
+ /* filename here is set on a redirect */
+ }
- default:
- printf("TFTP unknown opcode %d\n", ntohs(opcode));
- goto err_reply;
+ if (!found_scheme) {
+#if GPXE
+ /* No URL scheme found, hand it to GPXE */
+ gpxe_open(inode, urlsave);
+#endif
}
-done:
- if (!inode->size) {
+ if (inode->size) {
+ file->inode = inode;
+ file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG;
+ } else {
free_socket(inode);
- return;
}
- file->inode = inode;
- return;
-err_reply:
- /* Build the TFTP error packet */
- tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
- printf("TFTP server sent an incomprehesible reply\n");
- kaboom();
+ return;
}
static int __pxe_chdir(struct fs_info *fs, const char *src,
- enum pxe_path_type path_type)
+ enum url_type url_type)
{
- if (path_type == PXE_RELATIVE)
+ if (url_type == URL_SUFFIX)
strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
- else if (path_type == PXE_HOMESERVER)
+ else if (url_type == URL_OLD_TFTP)
snprintf(fs->cwd_name, sizeof fs->cwd_name, "::%s", src);
else
strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
@@ -1031,7 +431,7 @@ static void get_prefix(void)
}
printf("TFTP prefix: %s\n", path_prefix);
- __pxe_chdir(this_fs, path_prefix, PXE_HOMESERVER);
+ __pxe_chdir(this_fs, path_prefix, URL_OLD_TFTP);
}
/*
@@ -1040,10 +440,8 @@ static void get_prefix(void)
static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
size_t bufsize)
{
- enum pxe_path_type path_type = pxe_path_type(src);
-
return snprintf(dst, bufsize, "%s%s",
- path_type == PXE_RELATIVE ? fs->cwd_name : "", src);
+ url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
}
/*
@@ -1052,9 +450,7 @@ static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
static int pxe_chdir(struct fs_info *fs, const char *src)
{
/* The cwd for PXE is just a text prefix */
- enum pxe_path_type path_type = pxe_path_type(src);
-
- __pxe_chdir(fs, src, path_type);
+ __pxe_chdir(fs, src, url_type(src));
dprintf("cwd = \"%s\"\n", fs->cwd_name);
return 0;
@@ -1078,7 +474,7 @@ static int pxe_open_config(struct com32_filedata *filedata)
get_prefix();
if (DHCPMagic & 0x02) {
/* We got a DHCP option, try it first */
- if (open_file(ConfigName, filedata) >= 0)
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
}
@@ -1088,23 +484,23 @@ static int pxe_open_config(struct com32_filedata *filedata)
config_file = stpcpy(ConfigName, cfgprefix);
/* Try loading by UUID */
- if (have_uuid) {
- strcpy(config_file, UUID_str);
- if (open_file(ConfigName, filedata) >= 0)
+ if (sysappend_strings[SYSAPPEND_SYSUUID]) {
+ strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
}
/* Try loading by MAC address */
- strcpy(config_file, MAC_str);
- if (open_file(ConfigName, filedata) >= 0)
+ strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
/* Nope, try hexadecimal IP prefixes... */
- uchexbytes(config_file, (uint8_t *)&IPInfo.myip, 4);
+ sprintf(config_file, "%08X", ntohl(IPInfo.myip));
last = &config_file[8];
while (tries) {
*last = '\0'; /* Zero-terminate string */
- if (open_file(ConfigName, filedata) >= 0)
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
last--; /* Drop one character */
tries--;
@@ -1112,7 +508,7 @@ static int pxe_open_config(struct com32_filedata *filedata)
/* Final attempt: "default" string */
strcpy(config_file, default_str);
- if (open_file(ConfigName, filedata) >= 0)
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
printf("%-68s\n", "Unable to locate configuration file");
@@ -1124,43 +520,17 @@ static int pxe_open_config(struct com32_filedata *filedata)
*/
static void make_bootif_string(void)
{
+ static char bootif_str[7+3*(MAC_MAX+1)];
const uint8_t *src;
- char *dst = BOOTIFStr;
+ char *dst = bootif_str;
int i;
dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
src = MAC;
for (i = MAC_len; i; i--)
dst += sprintf(dst, "-%02x", *src++);
-}
-/*
- * Generate the SYSUUID string, if we have one...
- */
-static void make_sysuuid_string(void)
-{
- static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
- const uint8_t *src = uuid;
- const uint8_t *uuid_ptr = uuid_dashes;
- char *dst;
- SYSUUIDStr[0] = '\0'; /* If nothing there... */
-
- /* Try loading by UUID */
- if (have_uuid) {
- dst = stpcpy(SYSUUIDStr, "SYSUUID=");
-
- while (*uuid_ptr) {
- int len = *uuid_ptr;
-
- lchexbytes(dst, src, len);
- dst += len * 2;
- src += len;
- uuid_ptr++;
- *dst++ = '-';
- }
- /* Remove last dash and zero-terminate */
- *--dst = '\0';
- }
+ sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str;
}
/*
@@ -1168,21 +538,22 @@ static void make_sysuuid_string(void)
* option into IPOption based on DHCP information in IPInfo.
*
*/
-char __bss16 IPOption[3+4*16];
-
static void genipopt(void)
{
- char *p = IPOption;
+ static char ip_option[3+4*16];
const uint32_t *v = &IPInfo.myip;
+ char *p;
int i;
- p = stpcpy(p, "ip=");
+ p = stpcpy(ip_option, "ip=");
for (i = 0; i < 4; i++) {
p += gendotquad(p, *v++);
*p++ = ':';
}
*--p = '\0';
+
+ sysappend_strings[SYSAPPEND_IP] = ip_option;
}
@@ -1190,6 +561,7 @@ static void genipopt(void)
static void ip_init(void)
{
uint32_t ip = IPInfo.myip;
+ char dot_quad_buf[16];
genipopt();
gendotquad(dot_quad_buf, ip);
@@ -1199,20 +571,6 @@ static void ip_init(void)
}
/*
- * Print the IPAPPEND strings, in order
- */
-static void print_ipappend(void)
-{
- size_t i;
-
- for (i = 0; i < (size_t)numIPAppends; i++) {
- const char *p = (const char *)(size_t)IPAppends[i];
- if (*p)
- printf("%s\n", p);
- }
-}
-
-/*
* Validity check on possible !PXE structure in buf
* return 1 for success, 0 for failure.
*
@@ -1413,6 +771,8 @@ static int pxe_init(bool quiet)
real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
+ probe_undi();
+
return 0;
}
@@ -1437,24 +797,6 @@ static void gpxe_init(void)
}
/*
- * Initialize UDP stack
- *
- */
-static void udp_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();
- }
-}
-
-
-/*
* Network-specific initialization
*/
static void network_init(void)
@@ -1514,10 +856,12 @@ static void network_init(void)
lfree(bp);
make_bootif_string();
- make_sysuuid_string();
+ /* If DMI and DHCP disagree, which one should we set? */
+ if (have_uuid)
+ sysappend_set_uuid(uuid);
ip_init();
- print_ipappend();
+ /* 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.
@@ -1525,7 +869,7 @@ static void network_init(void)
if ((DHCPMagic & 1) == 0)
DHCPMagic = 0;
- udp_init();
+ net_core_init();
}
/*
@@ -1536,9 +880,8 @@ static int pxe_fs_init(struct fs_info *fs)
{
(void)fs; /* drop the compile warning message */
- /* This block size is actually arbitrary... */
- fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
- fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
+ /* Prepare for handling pxe interrupts */
+ pxe_init_isr();
/* This block size is actually arbitrary... */
fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
@@ -1600,9 +943,9 @@ static inline bool is_efi(const struct efi_struct *efi)
return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
}
+#if 0
static void install_int18_hack(void)
{
-#if 0
static const uint8_t int18_hack[] =
{
0xcd, 0x18, /* int $0x18 */
@@ -1638,30 +981,8 @@ static void install_int18_hack(void)
*(uint16_t *)(dst+46) = InitStack.seg;
}
}
-#endif
-}
-
-int reset_pxe(void)
-{
- static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
- extern void gpxe_unload(void);
- int err = 0;
-
- pxe_idle_cleanup();
-
- pxe_call(PXENV_UDP_CLOSE, &udp_close);
-
- if (gpxe_funcs & 0x80) {
- /* gPXE special unload implemented */
- call16(gpxe_unload, &zero_regs, NULL);
-
- /* Locate the actual vendor stack... */
- err = pxe_init(true);
- }
-
- install_int18_hack();
- return err;
}
+#endif
/*
* This function unloads the PXE and UNDI stacks and
@@ -1670,6 +991,12 @@ int reset_pxe(void)
__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
};
@@ -1689,6 +1016,7 @@ __export void unload_pxe(uint16_t flags)
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();
@@ -1707,7 +1035,8 @@ __export void unload_pxe(uint16_t flags)
memset(&unload_call, 0, sizeof unload_call);
err = pxe_call(api, &unload_call);
if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
- dprintf("PXE unload API call %04x failed\n", api);
+ printf("PXE unload API call %04x failed: 0x%x\n",
+ api, unload_call.Status);
goto cant_free;
}
}
@@ -1739,6 +1068,17 @@ cant_free:
return;
}
+static int pxe_readdir(struct file *file, struct dirent *dirent)
+{
+ struct inode *inode = file->inode;
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->ops->readdir)
+ return socket->ops->readdir(inode, dirent);
+ else
+ return -1; /* No such operation */
+}
+
const struct fs_ops pxe_fs_ops = {
.fs_name = "pxe",
.fs_flags = FS_NODEV,
@@ -1751,4 +1091,5 @@ const struct fs_ops pxe_fs_ops = {
.mangle_name = pxe_mangle_name,
.chdir_start = pxe_chdir_start,
.open_config = pxe_open_config,
+ .readdir = pxe_readdir,
};
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index d4377581..2d4be3b1 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -1,7 +1,7 @@
/* -----------------------------------------------------------------------
*
* Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
- * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2009-2011 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
@@ -21,67 +21,21 @@
#define PXE_H
#include <syslinux/pxe_api.h>
-#include "fs.h" /* For MAX_OPEN, should go away */
+#include "fs.h" /* Mostly for FILENAME_MAX */
/*
* Some basic defines...
*/
-#define TFTP_PORT htons(69) /* Default TFTP port */
-#define TFTP_BLOCKSIZE_LG2 9
-#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
-#define PKTBUF_SIZE 2048 /* */
+#define PKTBUF_SIZE 2048 /* Used mostly by the gPXE backend */
#define is_digit(c) (((c) >= '0') && ((c) <= '9'))
-static inline bool is_hex(char c)
-{
- return (c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'F') ||
- (c >= 'a' && c <= 'f');
-}
-
-static inline int hexval(char c)
-{
- return (c >= 'A') ? (c & ~0x20) - 'A' + 10 : (c - '0');
-}
-
-/*
- * TFTP operation codes
- */
-#define TFTP_RRQ htons(1) // Read rest
-#define TFTP_WRQ htons(2) // Write rest
-#define TFTP_DATA htons(3) // Data packet
-#define TFTP_ACK htons(4) // ACK packet
-#define TFTP_ERROR htons(5) // ERROR packet
-#define TFTP_OACK htons(6) // OACK packet
-
-/*
- * TFTP error codes
- */
-#define TFTP_EUNDEF htons(0) // Unspecified error
-#define TFTP_ENOTFOUND htons(1) // File not found
-#define TFTP_EACCESS htons(2) // Access violation
-#define TFTP_ENOSPACE htons(3) // Disk full
-#define TFTP_EBADOP htons(4) // Invalid TFTP operation
-#define TFTP_EBADID htons(5) // Unknown transfer
-#define TFTP_EEXISTS htons(6) // File exists
-#define TFTP_ENOUSER htons(7) // No such user
-#define TFTP_EOPTNEG htons(8) // Option negotiation failure
-
-
#define BOOTP_OPTION_MAGIC htonl(0x63825363)
#define MAC_MAX 32
-/* Defines for DNS */
-#define DNS_PORT htons(53) /* Default DNS port */
-#define DNS_MAX_PACKET 512 /* Defined by protocol */
-#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */
-
-
/*
- * structures
+ * structures
*/
-
struct pxenv_t {
uint8_t signature[6]; /* PXENV+ */
uint16_t version;
@@ -149,22 +103,42 @@ struct bootp_t {
uint8_t options[1260]; /* Vendor options */
} __attribute__ ((packed));
+struct netconn;
+struct netbuf;
/*
* Our inode private information -- this includes the packet buffer!
*/
+struct pxe_conn_ops {
+ void (*fill_buffer)(struct inode *inode);
+ void (*close)(struct inode *inode);
+ int (*readdir)(struct inode *inode, struct dirent *dirent);
+};
+
+struct net_private {
+#ifdef IS_LPXELINUX
+ struct netconn *conn; /* lwip network connection */
+ struct netbuf *buf; /* lwip cached buffer */
+#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 {
- uint16_t tftp_localport; /* Local port number (0=not in us)*/
- uint16_t tftp_remoteport; /* Remote port number */
- uint32_t tftp_remoteip; /* Remote IP address */
- uint32_t tftp_filepos; /* bytes downloaded (includeing buffer) */
- uint32_t tftp_blksize; /* Block size for this connection(*) */
- uint16_t tftp_bytesleft; /* Unclaimed data bytes */
- uint16_t tftp_lastpkt; /* Sequence number of last packet (NBO) */
- 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[PKTBUF_SIZE];
-} __attribute__ ((packed));
+ 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;
+};
#define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt))
@@ -184,6 +158,9 @@ struct ip_info {
*/
extern struct ip_info IPInfo;
+extern t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
+extern t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
+
extern uint8_t MAC[];
extern char BOOTIFStr[];
extern uint8_t MAC_len;
@@ -196,9 +173,6 @@ extern char boot_file[];
extern char path_prefix[];
extern char LocalDomain[];
-extern char IPOption[];
-extern char dot_quad_buf[];
-
extern uint32_t dns_server[];
extern uint16_t APIVer;
@@ -211,8 +185,6 @@ extern bool have_uuid;
extern uint8_t uuid_type;
extern uint8_t uuid[];
-extern const uint8_t TimeoutTable[];
-
/*
* Compute the suitable gateway for a specific route -- too many
* vendor PXE stacks don't do this correctly...
@@ -226,24 +198,62 @@ static inline uint32_t gateway(uint32_t ip)
}
/*
- * functions
+ * functions
*/
+/* pxeisr.inc */
+extern uint8_t pxe_irq_vector;
+extern void pxe_isr(void);
+extern far_ptr_t pxe_irq_chain;
+extern void pxe_poll(void);
+
+/* isr.c */
+void pxe_init_isr(void);
+void pxe_start_isr(void);
+int reset_pxe(void);
+
/* pxe.c */
+struct url_info;
bool ip_ok(uint32_t);
+int pxe_getc(struct inode *inode);
+void free_socket(struct inode *inode);
+
+/* undiif.c */
+int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw);
+void undiif_input(t_PXENV_UNDI_ISR *isr);
/* dhcp_options.c */
void parse_dhcp(const void *, size_t);
-/* dnsresolv.c */
-int dns_mangle(char **, const char *);
-
/* idle.c */
void pxe_idle_init(void);
void pxe_idle_cleanup(void);
-/* socknum.c */
-uint16_t get_port(void);
-void free_port(uint16_t port);
+/* tftp.c */
+void tftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* gpxeurl.c */
+void gpxe_open(struct inode *inode, const char *url);
+#define GPXE 0
+
+/* http.c */
+void http_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* http_readdir.c */
+int http_readdir(struct inode *inode, struct dirent *dirent);
+
+/* ftp.c */
+void ftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* ftp_readdir.c */
+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;
#endif /* pxe.h */
diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c
new file mode 100644
index 00000000..528fbce8
--- /dev/null
+++ b/core/fs/pxe/tcp.c
@@ -0,0 +1,78 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * tcp.c
+ *
+ * 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->private.conn) {
+ netconn_delete(socket->private.conn);
+ socket->private.conn = NULL;
+ }
+ if (socket->private.buf) {
+ netbuf_delete(socket->private.buf);
+ socket->private.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->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->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;
+ socket->ops->close(inode);
+ return;
+ }
+ }
+ /* Report the current fragment of the netbuf */
+ err = netbuf_data(socket->private.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;
+}
+
+const struct pxe_conn_ops tcp_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = tcp_close_file,
+};
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
new file mode 100644
index 00000000..9b755c93
--- /dev/null
+++ b/core/fs/pxe/tftp.c
@@ -0,0 +1,443 @@
+#include <minmax.h>
+#include <net.h>
+#include "pxe.h"
+#include "url.h"
+#include "tftp.h"
+
+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
+};
+struct tftp_options {
+ const char *str_ptr; /* string pointer */
+ size_t offset; /* offset into socket structre */
+};
+struct tftp_packet {
+ uint16_t opcode;
+ uint16_t serial;
+ char data[];
+};
+
+#define IFIELD(x) offsetof(struct inode, x)
+#define PFIELD(x) (offsetof(struct inode, pvt) + \
+ offsetof(struct pxe_pvt_inode, x))
+
+static const struct tftp_options tftp_options[] =
+{
+ { "tsize", IFIELD(size) },
+ { "blksize", PFIELD(tftp_blksize) },
+};
+static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
+
+static void tftp_error(struct inode *file, uint16_t errnum,
+ const char *errstr);
+
+static void tftp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ if (!socket->tftp_goteof) {
+ tftp_error(inode, 0, "No error, file close");
+ }
+ net_core_close(socket);
+}
+
+/**
+ * Send an ERROR packet. This is used to terminate a connection.
+ *
+ * @inode: Inode structure
+ * @errnum: Error number (network byte order)
+ * @errstr: Error string (included in packet)
+ */
+static void tftp_error(struct inode *inode, uint16_t errnum,
+ const char *errstr)
+{
+ static struct {
+ uint16_t err_op;
+ uint16_t err_num;
+ char err_msg[64];
+ } __packed err_buf;
+ int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ err_buf.err_op = TFTP_ERROR;
+ err_buf.err_num = errnum;
+ memcpy(err_buf.err_msg, errstr, len);
+ err_buf.err_msg[len] = '\0';
+
+ net_core_send(socket, &err_buf, 4 + len + 1);
+}
+
+/**
+ * Send ACK packet. This is a common operation and so is worth canning.
+ *
+ * @param: inode, Inode pointer
+ * @param: ack_num, Packet # to ack (host byte order)
+ *
+ */
+static void ack_packet(struct inode *inode, uint16_t ack_num)
+{
+ static uint16_t ack_packet_buf[2];
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ /* Packet number to ack */
+ ack_packet_buf[0] = TFTP_ACK;
+ ack_packet_buf[1] = htons(ack_num);
+
+ net_core_send(socket, ack_packet_buf, 4);
+}
+
+/*
+ * Get a fresh packet if the buffer is drained, and we haven't hit
+ * EOF yet. The buffer should be filled immediately after draining!
+ */
+static void tftp_get_packet(struct inode *inode)
+{
+ uint16_t last_pkt;
+ const uint8_t *timeout_ptr;
+ uint8_t timeout;
+ uint16_t buffersize;
+ uint16_t serial;
+ jiffies_t oldtime;
+ struct tftp_packet *pkt = NULL;
+ uint16_t buf_len;
+ struct pxe_pvt_inode *socket = PVT(inode);
+ uint16_t src_port;
+ uint32_t src_ip;
+ int err;
+
+ /*
+ * Start by ACKing the previous packet; this should cause
+ * the next packet to be sent.
+ */
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ oldtime = jiffies();
+
+ ack_again:
+ ack_packet(inode, socket->tftp_lastpkt);
+
+ while (timeout) {
+ 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) {
+ oldtime = now;
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ break;
+ goto ack_again;
+ }
+ continue;
+ }
+
+ if (buf_len < 4) /* Bad size for a DATA packet */
+ continue;
+
+ pkt = (struct tftp_packet *)(socket->tftp_pktbuf);
+ if (pkt->opcode != TFTP_DATA) /* Not a data packet */
+ continue;
+
+ /* If goes here, recevie OK, break */
+ break;
+ }
+
+ /* time runs out */
+ if (timeout == 0)
+ kaboom();
+
+ last_pkt = socket->tftp_lastpkt;
+ last_pkt++;
+ serial = ntohs(pkt->serial);
+ if (serial != last_pkt) {
+ /*
+ * Wrong packet, ACK the packet and try again.
+ * This is presumably because the ACK got lost,
+ * so the server just resent the previous packet.
+ */
+#if 0
+ printf("Wrong packet, wanted %04x, got %04x\n", \
+ htons(last_pkt), htons(*(uint16_t *)(data+2)));
+#endif
+ goto ack_again;
+ }
+
+ /* It's the packet we want. We're also EOF if the size < blocksize */
+ socket->tftp_lastpkt = last_pkt; /* Update last packet number */
+ buffersize = buf_len - 4; /* Skip TFTP header */
+ socket->tftp_dataptr = socket->tftp_pktbuf + 4;
+ socket->tftp_filepos += buffersize;
+ socket->tftp_bytesleft = buffersize;
+ if (buffersize < socket->tftp_blksize) {
+ /* it's the last block, ACK packet immediately */
+ ack_packet(inode, serial);
+
+ /* Make sure we know we are at end of file */
+ inode->size = socket->tftp_filepos;
+ socket->tftp_goteof = 1;
+ tftp_close_file(inode);
+ }
+}
+
+const struct pxe_conn_ops tftp_conn_ops = {
+ .fill_buffer = tftp_get_packet,
+ .close = tftp_close_file,
+};
+
+/**
+ * Open a TFTP connection to the server
+ *
+ * @param:inode, the inode to store our state in
+ * @param:ip, the ip to contact to get the file
+ * @param:filename, the file we wanna open
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+void tftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ char *buf;
+ uint16_t buf_len;
+ char *p;
+ char *options;
+ char *data;
+ static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
+ char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
+ char reply_packet_buf[PKTBUF_SIZE];
+ const struct tftp_options *tftp_opt;
+ int i = 0;
+ int err;
+ int buffersize;
+ int rrq_len;
+ const uint8_t *timeout_ptr;
+ jiffies_t timeout;
+ jiffies_t oldtime;
+ uint16_t opcode;
+ uint16_t blk_num;
+ uint32_t opdata, *opdata_ptr;
+ uint16_t src_port;
+ uint32_t src_ip;
+
+ (void)redir; /* TFTP does not redirect */
+ (void)flags;
+
+ if (url->type != URL_OLD_TFTP) {
+ /*
+ * The TFTP URL specification allows the TFTP to end with a
+ * ;mode= which we just ignore.
+ */
+ url_unescape(url->path, ';');
+ }
+
+ if (!url->port)
+ url->port = TFTP_PORT;
+
+ socket->ops = &tftp_conn_ops;
+ if (net_core_open(socket, NET_CORE_UDP))
+ return;
+
+ buf = rrq_packet_buf;
+ *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
+ buf += 2;
+
+ buf = stpcpy(buf, url->path);
+
+ buf++; /* Point *past* the final NULL */
+ memcpy(buf, rrq_tail, sizeof rrq_tail);
+ buf += sizeof rrq_tail;
+
+ rrq_len = buf - rrq_packet_buf;
+
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+sendreq:
+ net_core_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);
+
+ /* If the WRITE call fails, we let the timeout take care of it... */
+wait_pkt:
+ net_core_disconnect(socket);
+ for (;;) {
+ 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 */
+ if (src_ip == url->ip)
+ break;
+ }
+ }
+
+ net_core_disconnect(socket);
+ net_core_connect(socket, src_ip, src_port);
+
+ /* filesize <- -1 == unknown */
+ inode->size = -1;
+ socket->tftp_blksize = TFTP_BLOCKSIZE;
+ buffersize = buf_len - 2; /* bytes after opcode */
+ if (buffersize < 0)
+ goto wait_pkt; /* Garbled reply */
+
+ /*
+ * Get the opcode type, and parse it
+ */
+ opcode = *(uint16_t *)reply_packet_buf;
+ switch (opcode) {
+ case TFTP_ERROR:
+ inode->size = 0;
+ goto done; /* ERROR reply; don't try again */
+
+ case TFTP_DATA:
+ /*
+ * If the server doesn't support any options, we'll get a
+ * DATA reply instead of OACK. Stash the data in the file
+ * buffer and go with the default value for all options...
+ *
+ * We got a DATA packet, meaning no options are
+ * suported. Save the data away and consider the
+ * length undefined, *unless* this is the only
+ * data packet...
+ */
+ buffersize -= 2;
+ if (buffersize < 0)
+ goto wait_pkt;
+ data = reply_packet_buf + 2;
+ blk_num = ntohs(*(uint16_t *)data);
+ data += 2;
+ if (blk_num != 1)
+ goto wait_pkt;
+ socket->tftp_lastpkt = blk_num;
+ if (buffersize > TFTP_BLOCKSIZE)
+ goto err_reply; /* Corrupt */
+
+ socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4);
+ if (!socket->tftp_pktbuf)
+ goto err_reply; /* Internal error */
+
+ if (buffersize < TFTP_BLOCKSIZE) {
+ /*
+ * This is the final EOF packet, already...
+ * We know the filesize, but we also want to
+ * ack the packet and set the EOF flag.
+ */
+ inode->size = buffersize;
+ socket->tftp_goteof = 1;
+ ack_packet(inode, blk_num);
+ }
+
+ socket->tftp_bytesleft = buffersize;
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ memcpy(socket->tftp_pktbuf, data, buffersize);
+ goto done;
+
+ case TFTP_OACK:
+ /*
+ * Now we need to parse the OACK packet to get the transfer
+ * and packet sizes.
+ */
+
+ options = reply_packet_buf + 2;
+ p = options;
+
+ while (buffersize) {
+ const char *opt = p;
+
+ /*
+ * If we find an option which starts with a NUL byte,
+ * (a null option), we're either seeing garbage that some
+ * TFTP servers add to the end of the packet, or we have
+ * no clue how to parse the rest of the packet (what is
+ * an option name and what is a value?) In either case,
+ * discard the rest.
+ */
+ if (!*opt)
+ goto done;
+
+ while (buffersize) {
+ if (!*p)
+ break; /* Found a final null */
+ *p++ |= 0x20;
+ buffersize--;
+ }
+ if (!buffersize)
+ break; /* Unterminated option */
+
+ /* Consume the terminal null */
+ p++;
+ buffersize--;
+
+ if (!buffersize)
+ break; /* No option data */
+
+ /*
+ * Parse option pointed to by options; guaranteed to be
+ * null-terminated
+ */
+ tftp_opt = tftp_options;
+ for (i = 0; i < tftp_nopts; i++) {
+ if (!strcmp(opt, tftp_opt->str_ptr))
+ break;
+ tftp_opt++;
+ }
+ if (i == tftp_nopts)
+ goto err_reply; /* Non-negotitated option returned,
+ no idea what it means ...*/
+
+ /* get the address of the filed that we want to write on */
+ opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
+ opdata = 0;
+
+ /* do convert a number-string to decimal number, just like atoi */
+ while (buffersize--) {
+ uint8_t d = *p++;
+ if (d == '\0')
+ break; /* found a final null */
+ d -= '0';
+ if (d > 9)
+ goto err_reply; /* Not a decimal digit */
+ opdata = opdata*10 + d;
+ }
+ *opdata_ptr = opdata;
+ }
+
+ if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE)
+ goto err_reply;
+
+ /* Parsing successful, allocate buffer */
+ socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4);
+ if (!socket->tftp_pktbuf)
+ goto err_reply;
+ else
+ goto done;
+
+ default:
+ printf("TFTP unknown opcode %d\n", ntohs(opcode));
+ goto err_reply;
+ }
+
+err_reply:
+ /* Build the TFTP error packet */
+ tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
+ inode->size = 0;
+
+done:
+ if (!inode->size)
+ net_core_close(socket);
+
+ return;
+}
diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h
new file mode 100644
index 00000000..114c221f
--- /dev/null
+++ b/core/fs/pxe/tftp.h
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2011 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., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * tftp.h
+ */
+#ifndef PXE_TFTP_H
+#define PXE_TFTP_H
+
+/*
+ * TFTP default port number
+ */
+#define TFTP_PORT 69
+
+/*
+ * TFTP default block size
+ */
+#define TFTP_BLOCKSIZE_LG2 9
+#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
+
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ htons(1) // Read rest
+#define TFTP_WRQ htons(2) // Write rest
+#define TFTP_DATA htons(3) // Data packet
+#define TFTP_ACK htons(4) // ACK packet
+#define TFTP_ERROR htons(5) // ERROR packet
+#define TFTP_OACK htons(6) // OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF htons(0) // Unspecified error
+#define TFTP_ENOTFOUND htons(1) // File not found
+#define TFTP_EACCESS htons(2) // Access violation
+#define TFTP_ENOSPACE htons(3) // Disk full
+#define TFTP_EBADOP htons(4) // Invalid TFTP operation
+#define TFTP_EBADID htons(5) // Unknown transfer
+#define TFTP_EEXISTS htons(6) // File exists
+#define TFTP_ENOUSER htons(7) // No such user
+#define TFTP_EOPTNEG htons(8) // Option negotiation failure
+
+#endif /* PXE_TFTP_H */
diff --git a/core/fs/pxe/url.h b/core/fs/pxe/url.h
new file mode 100644
index 00000000..53984f3a
--- /dev/null
+++ b/core/fs/pxe/url.h
@@ -0,0 +1,33 @@
+/*
+ * url.h
+ */
+
+#ifndef CORE_PXE_URL_H
+#define CORE_PXE_URL_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+enum url_type {
+ URL_NORMAL, /* It is a full URL */
+ URL_OLD_TFTP, /* It's a ::-style TFTP path */
+ URL_SUFFIX /* Prepend the pathname prefix */
+};
+
+struct url_info {
+ char *scheme;
+ char *user;
+ char *passwd;
+ char *host;
+ uint32_t ip; /* Placeholder field not set by parse_url() */
+ unsigned int port;
+ char *path; /* Includes query */
+ enum url_type type;
+};
+
+enum url_type url_type(const char *url);
+void parse_url(struct url_info *ui, char *url);
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize);
+char *url_unescape(char *buffer, char terminator);
+
+#endif /* CORE_PXE_URL_H */
diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c
new file mode 100644
index 00000000..6b73ddb6
--- /dev/null
+++ b/core/fs/pxe/urlparse.c
@@ -0,0 +1,223 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * urlparse.c
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "url.h"
+
+/*
+ * Return the type of a URL without modifying the string
+ */
+enum url_type url_type(const char *url)
+{
+ const char *q;
+
+ q = strchr(url, ':');
+ if (!q)
+ return URL_SUFFIX;
+
+ if (q[1] == '/' && q[2] == '/')
+ return URL_NORMAL;
+
+ if (q[1] == ':')
+ return URL_OLD_TFTP;
+
+ return URL_SUFFIX;
+}
+
+/*
+ * Decompose a URL into its components. This is done in-place;
+ * this routine does not allocate any additional storage. Freeing the
+ * original buffer frees all storage used.
+ */
+void parse_url(struct url_info *ui, char *url)
+{
+ char *p = url;
+ char *q, *r, *s;
+ int c;
+
+ memset(ui, 0, sizeof *ui);
+
+ q = strchr(p, ':');
+ if (q && (q[1] == '/' && q[2] == '/')) {
+ ui->type = URL_NORMAL;
+
+ ui->scheme = p;
+ *q = '\0';
+ p = q+3;
+
+ q = strchr(p, '/');
+ if (q) {
+ *q = '\0';
+ ui->path = q+1;
+ q = strchr(q+1, '#');
+ if (q)
+ *q = '\0';
+ } else {
+ ui->path = "";
+ }
+
+ r = strchr(p, '@');
+ if (r) {
+ ui->user = p;
+ *r = '\0';
+ s = strchr(p, ':');
+ if (s) {
+ *s = '\0';
+ ui->passwd = s+1;
+ }
+ p = r+1;
+ }
+
+ ui->host = p;
+ r = strchr(p, ':');
+ if (r) {
+ *r++ = '\0';
+ ui->port = 0;
+ while ((c = *r++)) {
+ c -= '0';
+ if (c > 9)
+ break;
+ ui->port = ui->port * 10 + c;
+ }
+ }
+ } else if (q && q[1] == ':') {
+ *q = '\0';
+ ui->scheme = "tftp";
+ ui->host = p;
+ ui->path = q+2;
+ ui->type = URL_OLD_TFTP;
+ } else {
+ ui->path = p;
+ ui->type = URL_SUFFIX;
+ }
+}
+
+/*
+ * Escapes unsafe characters in a URL.
+ * This does *not* escape things like query characters!
+ * Returns the number of characters in the total output.
+ */
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize)
+{
+ static const char uchexchar[] = "0123456789ABCDEF";
+ const char *p;
+ unsigned char c;
+ char *q;
+ size_t n = 0;
+
+ q = output;
+ for (p = input; (c = *p); p++) {
+ if (c <= ' ' || c > '~') {
+ if (++n < bufsize) *q++ = '%';
+ if (++n < bufsize) *q++ = uchexchar[c >> 4];
+ if (++n < bufsize) *q++ = uchexchar[c & 15];
+ } else {
+ if (++n < bufsize) *q++ = c;
+ }
+ }
+
+ *q = '\0';
+ return n;
+}
+
+static int hexdigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ c |= 0x20;
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+/*
+ * Unescapes a buffer, optionally ending at an *unescaped* terminator
+ * (like ; for TFTP). The unescaping is done in-place.
+ *
+ * If a terminator is reached, return a pointer to the first character
+ * after the terminator.
+ */
+char *url_unescape(char *buffer, char terminator)
+{
+ char *p = buffer;
+ char *q = buffer;
+ unsigned char c;
+ int x, y;
+
+ while ((c = *p)) {
+ if (c == terminator) {
+ *q = '\0';
+ return p;
+ }
+ p++;
+ if (c == '%') {
+ x = hexdigit(p[0]);
+ if (x >= 0) {
+ y = hexdigit(p[1]);
+ if (y >= 0) {
+ *q++ = (x << 4) + y;
+ p += 2;
+ continue;
+ }
+ }
+ }
+ *q++ = c;
+ }
+ *q = '\0';
+ return NULL;
+}
+
+#ifdef URL_TEST
+
+int main(int argc, char *argv[])
+{
+ int i;
+ struct url_info url;
+
+ for (i = 1; i < argc; i++) {
+ parse_url(&url, argv[i]);
+ printf("scheme: %s\n"
+ "user: %s\n"
+ "passwd: %s\n"
+ "host: %s\n"
+ "port: %d\n"
+ "path: %s\n"
+ "type: %d\n",
+ url.scheme, url.user, url.passwd, url.host, url.port,
+ url.path, url.type);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/core/fs/readdir.c b/core/fs/readdir.c
index e2d593fc..546a704a 100644
--- a/core/fs/readdir.c
+++ b/core/fs/readdir.c
@@ -1,3 +1,4 @@
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/dirent.h>
@@ -12,7 +13,7 @@ __export DIR *opendir(const char *path)
int rv;
struct file *file;
- rv = searchdir(path);
+ rv = searchdir(path, O_RDONLY|O_DIRECTORY);
if (rv < 0)
return NULL;