From 87fa53c543531231cd54ab4857d5e21f76555265 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 7 Apr 2011 22:52:57 -0700 Subject: core: declare jiffies_t and mstime_t and use them. Create specific types for jiffies and for the output of the millisecond timer and use them where appropriate in the code base. Signed-off-by: Eric W. Biederman --- core/fs/pxe/dnsresolv.c | 2 +- core/fs/pxe/pxe.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c index 641ea389..06d5a73f 100644 --- a/core/fs/pxe/dnsresolv.c +++ b/core/fs/pxe/dnsresolv.c @@ -182,7 +182,7 @@ uint32_t dns_resolv(const char *name) int ques, reps; /* number of questions and replies */ uint8_t timeout; const uint8_t *timeout_ptr = TimeoutTable; - uint32_t oldtime; + jiffies_t oldtime; uint32_t srv; uint32_t *srv_ptr; struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 21d27e5c..262b38ef 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -485,7 +485,7 @@ static void fill_buffer(struct inode *inode) const uint8_t *timeout_ptr; uint8_t timeout; uint16_t buffersize; - uint32_t oldtime; + jiffies_t oldtime; void *data = NULL; static __lowmem struct s_PXENV_UDP_READ udp_read; struct pxe_pvt_inode *socket = PVT(inode); @@ -520,7 +520,7 @@ static void fill_buffer(struct inode *inode) udp_read.d_port = socket->tftp_localport; err = pxe_call(PXENV_UDP_READ, &udp_read); if (err) { - uint32_t now = jiffies(); + jiffies_t now = jiffies(); if (now-oldtime >= timeout) { oldtime = now; @@ -678,8 +678,8 @@ static void __pxe_searchdir(const char *filename, struct file *file) int buffersize; int rrq_len; const uint8_t *timeout_ptr; - uint32_t timeout; - uint32_t oldtime; + jiffies_t timeout; + jiffies_t oldtime; uint16_t tid; uint16_t opcode; uint16_t blk_num; @@ -822,7 +822,7 @@ wait_pkt: udp_read.d_port = tid; err = pxe_call(PXENV_UDP_READ, &udp_read); if (err || udp_read.status) { - uint32_t now = jiffies(); + jiffies_t now = jiffies(); if (now - oldtime >= timeout) goto sendreq; } else { -- cgit v1.2.1 From d6103068834b100a2a636ce0d9f2dfc727829c4f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 10 Sep 2009 17:51:05 -0700 Subject: core: pxe: pm return hook to examine timer ticks and PXE ISR status When returning to protected mode, check to see if we have any pending actions due to interrupts. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 core/fs/pxe/isr.c (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c new file mode 100644 index 00000000..105f971f --- /dev/null +++ b/core/fs/pxe/isr.c @@ -0,0 +1,44 @@ +/* + * 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" + +extern uint8_t pxe_irq_pending; +struct semaphore pxe_receive_thread_sem; + +static void pm_return(void) +{ + static uint32_t last_jiffies = 0; + uint32_t now = jiffies(); + + __schedule_lock++; + + if (now != last_jiffies) { + last_jiffies = now; + __thread_process_timeouts(); + } + + if (pxe_irq_pending) { + pxe_irq_pending = 0; + sem_up(&pxe_receive_thread_sem); + } + + __schedule_lock--; + + if (__need_schedule) + __schedule(); +} + +void pxe_init_isr(void) +{ + start_idle_thread(); + sem_init(&pxe_receive_thread_sem, 0); + core_pm_hook = pm_return; +} + + -- cgit v1.2.1 From 6caad52d5cf86a770c87af0c252503a83e7b020f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 7 Apr 2011 23:50:31 -0700 Subject: core: pxe: pm_return Use jiffies_t Signed-off-by: Eric W. Biederman --- core/fs/pxe/isr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 105f971f..e6567402 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -13,8 +13,8 @@ struct semaphore pxe_receive_thread_sem; static void pm_return(void) { - static uint32_t last_jiffies = 0; - uint32_t now = jiffies(); + static jiffies_t last_jiffies = 0; + jiffies_t now = jiffies(); __schedule_lock++; @@ -40,5 +40,3 @@ void pxe_init_isr(void) sem_init(&pxe_receive_thread_sem, 0); core_pm_hook = pm_return; } - - -- cgit v1.2.1 From 783d74ea5d4ffe0addb67ce88315e9c9bd91da5f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 9 Apr 2011 01:36:10 -0700 Subject: pxe: Statically initialize pxe_receive_thread_sem Signed-off-by: Eric W. Biederman --- core/fs/pxe/isr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index e6567402..ae5fab74 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -9,7 +9,7 @@ #include "thread.h" extern uint8_t pxe_irq_pending; -struct semaphore pxe_receive_thread_sem; +static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); static void pm_return(void) { @@ -37,6 +37,5 @@ static void pm_return(void) void pxe_init_isr(void) { start_idle_thread(); - sem_init(&pxe_receive_thread_sem, 0); core_pm_hook = pm_return; } -- cgit v1.2.1 From 475366e7045de381c9d038faaa535df84bb03ba9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 8 Apr 2011 02:13:15 -0700 Subject: pxe: Add methods to abstract out the connection type. The gpxe handling has already found the places where we need to have different code to handle different connection types. So add function pointers into struct pxe_pvt_inode for each of those places and cleanup the code to call those new functions. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 329 +++++++++++++++++++++++++++++++----------------------- core/fs/pxe/pxe.h | 2 + 2 files changed, 191 insertions(+), 140 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 262b38ef..9c40fa11 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -93,20 +93,21 @@ static void gpxe_close_file(struct inode *inode) } #endif +static void tftp_close_file(struct inode *inode) +{ + struct pxe_pvt_inode *socket = PVT(inode); + if (socket->tftp_localport != 0) { + tftp_error(inode, 0, "No error, file close"); + } +} + static void pxe_close_file(struct file *file) { struct inode *inode = file->inode; struct pxe_pvt_inode *socket = PVT(inode); 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->close(inode); } free_socket(inode); @@ -421,7 +422,7 @@ static enum pxe_path_type pxe_path_type(const char *str) * @param: inode -> Inode pointer * */ -static void get_packet_gpxe(struct inode *inode) +static void gpxe_get_packet(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); static __lowmem struct s_PXENV_FILE_READ file_read; @@ -478,7 +479,7 @@ 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! */ -static void fill_buffer(struct inode *inode) +static void tftp_get_packet(struct inode *inode) { int err; int last_pkt; @@ -490,16 +491,6 @@ static void fill_buffer(struct inode *inode) static __lowmem struct s_PXENV_UDP_READ udp_read; struct pxe_pvt_inode *socket = PVT(inode); - if (socket->tftp_bytesleft || socket->tftp_goteof) - return; - -#if GPXE - if (socket->tftp_localport == 0xffff) { - get_packet_gpxe(inode); - return; - } -#endif - /* * Start by ACKing the previous packet; this should cause * the next packet to be sent. @@ -580,6 +571,19 @@ static void fill_buffer(struct inode *inode) } } +/* + * 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; + + return socket->fill_buffer(inode); +} + /** * getfssec: Get multiple clusters from a file, given the starting cluster. @@ -634,42 +638,58 @@ static uint32_t pxe_getfssec(struct file *file, char *buf, return bytes_read; } +#if GPXE /** - * Open a TFTP connection to the server + * Open a url using gpxe * - * @param:filename, the file we wanna open + * @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 * */ -static void __pxe_searchdir(const char *filename, struct file *file); -extern uint16_t PXERetry; - -static void pxe_searchdir(const char *filename, struct file *file) +static void gpxe_open(struct inode *inode, const char *url) { - int i = PXERetry; + static __lowmem struct s_PXENV_FILE_OPEN file_open; + static char lowurl[2*FILENAME_MAX]; + struct pxe_pvt_inode *socket = PVT(inode); + int err; - do { - dprintf("PXE: file = %p, retries left = %d: ", file, i); - __pxe_searchdir(filename, file); - dprintf("%s\n", file->inode ? "ok" : "failed"); - } while (!file->inode && i--); + 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 -static void __pxe_searchdir(const char *filename, struct file *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 + * + */ +static void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, const char *filename) { - struct fs_info *fs = file->fs; - struct inode *inode; - struct pxe_pvt_inode *socket; + struct pxe_pvt_inode *socket = PVT(inode); 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; @@ -683,76 +703,16 @@ static void __pxe_searchdir(const char *filename, struct file *file) 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 */ - inode = file->inode = NULL; - + socket->fill_buffer = tftp_get_packet; + socket->close = tftp_close_file; + 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 = stpcpy(buf, filename); buf++; /* Point *past* the final NULL */ memcpy(buf, rrq_tail, sizeof rrq_tail); @@ -760,38 +720,6 @@ static void __pxe_searchdir(const char *filename, struct file *file) rrq_len = buf - rrq_packet_buf; - inode = allocate_socket(fs); - if (!inode) - return; /* Allocation failure */ - socket = PVT(inode); - -#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; - } - } - } -#endif /* GPXE */ - - if (!ip) - goto done; /* No server */ - timeout_ptr = TimeoutTable; /* Reset timeout */ sendreq: @@ -964,13 +892,7 @@ wait_pkt: printf("TFTP unknown opcode %d\n", ntohs(opcode)); goto err_reply; } - done: - if (!inode->size) { - free_socket(inode); - return; - } - file->inode = inode; return; err_reply: @@ -980,6 +902,133 @@ err_reply: kaboom(); } +/** + * Open the specified connection + * + * @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 + * + */ +static void __pxe_searchdir(const char *filename, struct file *file); +extern uint16_t PXERetry; + +static void pxe_searchdir(const char *filename, struct file *file) +{ + int i = PXERetry; + + do { + dprintf("PXE: file = %p, retries left = %d: ", file, i); + __pxe_searchdir(filename, file); + dprintf("%s\n", file->inode ? "ok" : "failed"); + } while (!file->inode && i--); +} +static void __pxe_searchdir(const char *filename, struct file *file) +{ + struct fs_info *fs = file->fs; + struct inode *inode; + const char *np; + char *buf; + uint32_t ip = 0; + enum pxe_path_type path_type; + char fullpath[2*FILENAME_MAX]; + uint16_t server_port = TFTP_PORT; /* TFTP server port */ + + inode = file->inode = NULL; + + 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: + ip = IPInfo.serverip; /* Default server */ + break; + + case PXE_HOMESERVER: + filename = filename+2; + ip = IPInfo.serverip; + break; + + case PXE_TFTP: + np = strchr(filename, ':'); + if (parse_dotquad(filename, &ip) != np) + ip = dns_resolv(filename); + filename = np+2; + 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. + */ + filename = buf = fullpath; + while (*np && *np != ';') { + int v; + if (*np == '%' && (v = hexbyte(np+1)) > 0) { + *buf++ = v; + np += 3; + } else { + *buf++ = *np++; + } + } + *buf = '\0'; + break; + } + + inode = allocate_socket(fs); + if (!inode) + return; /* Allocation failure */ + +#if GPXE + if (path_type == PXE_URL) { + if (has_gpxe) { + gpxe_open(inode, filename); + goto done; + } else { + static bool already = false; + if (!already) { + printf("URL syntax, but gPXE extensions not detected, " + "trying plain TFTP...\n"); + already = true; + } + } + } +#endif /* GPXE */ + + if (!ip) + goto done; /* No server */ + + tftp_open(inode, ip, server_port, filename); +done: + if (!inode->size) { + free_socket(inode); + return; + } + file->inode = inode; + return; +} + /* * Store standard filename prefix diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 1e6fa76a..b199ea12 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -163,6 +163,8 @@ struct pxe_pvt_inode { char *tftp_dataptr; /* Pointer to available data */ uint8_t tftp_goteof; /* 1 if the EOF packet received */ uint8_t tftp_unused[3]; /* Currently unused */ + void (*fill_buffer)(struct inode *inode); + void (*close)(struct inode *inode); char tftp_pktbuf[PKTBUF_SIZE]; } __attribute__ ((packed)); -- cgit v1.2.1 From 6df2f637d26aa00b48d624f6fbf7ba406b989912 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 8 Apr 2011 02:53:28 -0700 Subject: pxe: Move the tftp implementation into it's own file Now that we have method pointers there is no longer any reason to clutter up pxe.c with tftp specific details. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 430 +---------------------------------------------------- core/fs/pxe/pxe.h | 5 + core/fs/pxe/tftp.c | 429 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 435 insertions(+), 429 deletions(-) create mode 100644 core/fs/pxe/tftp.c (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 9c40fa11..18f6c0dd 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -29,31 +29,7 @@ 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 const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0]; - -static void tftp_error(struct inode *file, uint16_t errnum, - const char *errstr); +__lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); /* * Allocate a local UDP port structure and assign it a local port number. @@ -93,14 +69,6 @@ static void gpxe_close_file(struct inode *inode) } #endif -static void tftp_close_file(struct inode *inode) -{ - struct pxe_pvt_inode *socket = PVT(inode); - if (socket->tftp_localport != 0) { - tftp_error(inode, 0, "No error, file close"); - } -} - static void pxe_close_file(struct file *file) { struct inode *inode = file->inode; @@ -272,74 +240,6 @@ int pxe_call(int opcode, void *data) 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; - - /* 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 -} - - /** * Get a DHCP packet from the PXE stack into the trackbuf * @@ -475,102 +375,6 @@ static void pxe_mangle_name(char *dst, const char *src) *dst = '\0'; } -/* - * 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) -{ - int err; - int last_pkt; - const uint8_t *timeout_ptr; - uint8_t timeout; - uint16_t buffersize; - jiffies_t oldtime; - void *data = NULL; - static __lowmem struct s_PXENV_UDP_READ udp_read; - struct pxe_pvt_inode *socket = PVT(inode); - - /* - * 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) { - jiffies_t now = jiffies(); - - if (now-oldtime >= timeout) { - oldtime = now; - timeout = *timeout_ptr++; - if (!timeout) - break; - } - continue; - } - - 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(); - - 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; - } - - /* 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; - } -} - /* * Get a fresh packet if the buffer is drained, and we haven't hit * EOF yet. The buffer should be filled immediately after draining! @@ -670,238 +474,6 @@ static void gpxe_open(struct inode *inode, const char *url) } #endif -/** - * 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 - * - */ -static void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, const char *filename) -{ - struct pxe_pvt_inode *socket = PVT(inode); - char *buf; - 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 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; - jiffies_t timeout; - jiffies_t oldtime; - uint16_t tid; - uint16_t opcode; - uint16_t blk_num; - uint32_t opdata, *opdata_ptr; - - socket->fill_buffer = tftp_get_packet; - socket->close = tftp_close_file; - - buf = rrq_packet_buf; - *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ - buf += 2; - - buf = stpcpy(buf, filename); - - 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: - timeout = *timeout_ptr++; - if (!timeout) - 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) { - jiffies_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) - 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; - - default: - printf("TFTP unknown opcode %d\n", ntohs(opcode)); - goto err_reply; - } -done: - 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(); -} - /** * Open the specified connection * diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index b199ea12..eaadc63f 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -235,6 +235,7 @@ static inline uint32_t gateway(uint32_t ip) /* pxe.c */ bool ip_ok(uint32_t); int pxe_call(int, void *); +extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); /* dhcp_options.c */ void parse_dhcp(int); @@ -251,4 +252,8 @@ void pxe_idle_cleanup(void); uint16_t get_port(void); void free_port(uint16_t port); +/* tftp.c */ +void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, + const char *filename); + #endif /* pxe.h */ diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c new file mode 100644 index 00000000..4fbe4058 --- /dev/null +++ b/core/fs/pxe/tftp.c @@ -0,0 +1,429 @@ +#include +#include "pxe.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 */ +}; + +#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_localport != 0) { + tftp_error(inode, 0, "No error, file close"); + } +} + +/** + * 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; + + /* 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 +} + +/* + * 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) +{ + int err; + int last_pkt; + const uint8_t *timeout_ptr; + uint8_t timeout; + uint16_t buffersize; + jiffies_t oldtime; + void *data = NULL; + static __lowmem struct s_PXENV_UDP_READ udp_read; + struct pxe_pvt_inode *socket = PVT(inode); + + /* + * 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) { + jiffies_t now = jiffies(); + + if (now-oldtime >= timeout) { + oldtime = now; + timeout = *timeout_ptr++; + if (!timeout) + break; + } + continue; + } + + 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(); + + 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; + } + + /* 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; + } +} + +/** + * 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 inode *inode, uint32_t ip, uint16_t server_port, + const char *filename) +{ + struct pxe_pvt_inode *socket = PVT(inode); + char *buf; + 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 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; + jiffies_t timeout; + jiffies_t oldtime; + uint16_t tid; + uint16_t opcode; + uint16_t blk_num; + uint32_t opdata, *opdata_ptr; + + socket->fill_buffer = tftp_get_packet; + socket->close = tftp_close_file; + + buf = rrq_packet_buf; + *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ + buf += 2; + + buf = stpcpy(buf, filename); + + 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: + timeout = *timeout_ptr++; + if (!timeout) + 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) { + jiffies_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) + 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; + + default: + printf("TFTP unknown opcode %d\n", ntohs(opcode)); + goto err_reply; + } +done: + 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(); +} + -- cgit v1.2.1 From 7a1d2e075ec8eef2aa311167101cc94c28f32b9d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 8 Apr 2011 03:01:26 -0700 Subject: pxe: Move the gpxe url reading code into it's own file. Now that we have method pointers there is no longer any reason to clutter up pxe.c with tftp specific details. Signed-off-by: Eric W. Biederman --- core/fs/pxe/gpxeurl.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ core/fs/pxe/pxe.c | 89 --------------------------------------------------- core/fs/pxe/pxe.h | 4 +++ 3 files changed, 89 insertions(+), 89 deletions(-) create mode 100644 core/fs/pxe/gpxeurl.c (limited to 'core/fs') diff --git a/core/fs/pxe/gpxeurl.c b/core/fs/pxe/gpxeurl.c new file mode 100644 index 00000000..16152818 --- /dev/null +++ b/core/fs/pxe/gpxeurl.c @@ -0,0 +1,85 @@ +#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; + + 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/pxe.c b/core/fs/pxe/pxe.c index 18f6c0dd..4bdd08a8 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -7,8 +7,6 @@ #include #include "pxe.h" -#define GPXE 1 - static uint16_t real_base_mem; /* Amount of DOS memory after freeing */ uint8_t MAC[MAC_MAX]; /* Actual MAC address */ @@ -58,17 +56,6 @@ static void free_socket(struct inode *inode) 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; @@ -315,51 +302,6 @@ static enum pxe_path_type pxe_path_type(const char *str) } } -#if GPXE - -/** - * 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); -} -#endif /* GPXE */ - - /* * mangle a filename pointed to by _src_ into a buffer pointed * to by _dst_; ends on encountering any whitespace. @@ -442,37 +384,6 @@ static uint32_t pxe_getfssec(struct file *file, char *buf, return bytes_read; } -#if GPXE -/** - * 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 - * - */ -static 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; - - 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 /** * Open the specified connection diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index eaadc63f..60feedb7 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -256,4 +256,8 @@ void free_port(uint16_t port); void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, const char *filename); +/* gpxeurl.c */ +void gpxe_open(struct inode *inode, const char *url); +#define GPXE 1 + #endif /* pxe.h */ -- cgit v1.2.1 From 4a9967816de45858b8afabd650e8122c7fc0c8b8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 9 Apr 2011 21:16:21 -0700 Subject: pxe: hook up the interrupt routine and the lwip receive routine Hook up the interrupt routine to the lwip receive routine; this should be able to receive packets as written. Signed-off-by: Eric W. Biederman --- core/fs/pxe/isr.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ core/fs/pxe/pxe.c | 3 +++ 2 files changed, 56 insertions(+) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index ae5fab74..882f41fd 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -7,6 +7,8 @@ #include "core.h" #include "thread.h" +#include "pxe.h" +#include extern uint8_t pxe_irq_pending; static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); @@ -34,8 +36,59 @@ static void pm_return(void) __schedule(); } +void undiif_input(t_PXENV_UNDI_ISR *); + +static void pxe_receive_thread(void *dummy) +{ + static __lowmem t_PXENV_UNDI_ISR isr; + uint16_t func; + bool done; + + (void)dummy; + + for (;;) { + sem_down(&pxe_receive_thread_sem, 0); + func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */ + + 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; + } + } + } +} + + void pxe_init_isr(void) { start_idle_thread(); + start_thread("pxe receive", 16384, 0, pxe_receive_thread, NULL); core_pm_hook = pm_return; } diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 4bdd08a8..d655526e 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1066,8 +1066,11 @@ static void network_init(void) */ static int pxe_fs_init(struct fs_info *fs) { + extern void pxe_init_isr(void); /* XXX */ (void)fs; /* drop the compile warning message */ + pxe_init_isr(); + /* 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; -- cgit v1.2.1 From 3adb3b89844db390a81fda2f8b8a4bf7253e0315 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 9 Apr 2011 02:25:51 -0700 Subject: core pxe: Switch threads if more than one runnable. Signed-off-by: Eric W. Biederman --- core/fs/pxe/isr.c | 1 + 1 file changed, 1 insertion(+) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 882f41fd..d645de2b 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -23,6 +23,7 @@ static void pm_return(void) if (now != last_jiffies) { last_jiffies = now; __thread_process_timeouts(); + __need_schedule = true; /* Switch threads if more than one runnable */ } if (pxe_irq_pending) { -- cgit v1.2.1 From a7831eb9ab81cf1bb5a7d0247eb9d682578a0335 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 15 Sep 2009 23:21:24 -0700 Subject: core: pxe: additional work on the lwip port Additional work on the lwip port. With this code, we can get pretty far before having problems. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 3 ++- core/fs/pxe/pxe.c | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index d645de2b..07990615 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -28,7 +28,8 @@ static void pm_return(void) if (pxe_irq_pending) { pxe_irq_pending = 0; - sem_up(&pxe_receive_thread_sem); + if (pxe_receive_thread_sem.count <= 0) + sem_up(&pxe_receive_thread_sem); } __schedule_lock--; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index d655526e..b886ed35 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -6,6 +6,9 @@ #include #include #include "pxe.h" +#if 1 +#include "lwip/api.h" +#endif static uint16_t real_base_mem; /* Amount of DOS memory after freeing */ @@ -1057,7 +1060,16 @@ static void network_init(void) if ((DHCPMagic & 1) == 0) DHCPMagic = 0; +#if 1 + extern err_t undi_tcpip_start(struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw); + undi_tcpip_start((struct ip_addr *)&IPInfo.myip, + (struct ip_addr *)&IPInfo.netmask, + (struct ip_addr *)&IPInfo.gateway); +#else udp_init(); +#endif } /* -- cgit v1.2.1 From 4f48e79e03961b9a1699464a8600422c17356a99 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 9 Apr 2011 03:55:50 -0700 Subject: core pxe: Ensure all parameters in pxe_get_cached_info are initialized. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index b886ed35..ebf3dc1a 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -243,7 +243,7 @@ static int pxe_get_cached_info(int type) 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 = 8192; get_cached_info.Buffer = FAR_PTR(trackbuf); -- cgit v1.2.1 From 699cc38092333d7326cbf32ae862d30c59f2c502 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 16 Sep 2009 17:48:26 -0700 Subject: lwip: now to the point we can make a TCP connection... lwip is now functional enough that TCP and DNS seem to work. More tests still need to be done, though. Signed-off-by: H. Peter Anvin Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index ebf3dc1a..14a767d5 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -8,6 +8,7 @@ #include "pxe.h" #if 1 #include "lwip/api.h" +#include "lwip/dns.h" #endif static uint16_t real_base_mem; /* Amount of DOS memory after freeing */ @@ -980,6 +981,7 @@ static void gpxe_init(void) has_gpxe = (~gpxe_funcs & 0x4b) == 0; } +#if 0 /* * Initialize UDP stack * @@ -996,7 +998,67 @@ static void udp_init(void) kaboom(); } } +#endif + +static void lwip_test(void) +{ + err_t err; + struct ip_addr ip; + struct netconn *conn; + char header_buf[512]; + int header_len; + static const char host_str[] = "www.zytor.com"; + struct netbuf *buf; + + /* Test the lwIP stack by trying to open a HTTP connection... */ + printf("Starting lwIP test...\n"); + err = netconn_gethostbyname(host_str, &ip); + printf("Gethostbyname: ip = %d.%d.%d.%d, err = %d\n", + ((uint8_t *)&ip)[0], + ((uint8_t *)&ip)[1], + ((uint8_t *)&ip)[2], + ((uint8_t *)&ip)[3], + err); + + conn = netconn_new(NETCONN_TCP); + printf("netconn_new returned %p\n", conn); + + err = netconn_connect(conn, &ip, 80); + printf("netconn_connect error %d\n", err); + + header_len = snprintf(header_buf, sizeof header_buf, + "GET /lwip_test HTTP/1.0\r\n" + "Host: %s\r\n" + "\r\n", + host_str); + + err = netconn_write(conn, header_buf, header_len, NETCONN_NOCOPY); + printf("netconn_write error %d\n", err); + for (;;) { + void *data; + char *p; + u16_t len; + + buf = netconn_recv(conn); + if (!buf) + break; + netbuf_data(buf, &data, &len); + + p = data; + while (len--) + putchar (*p++); + + netbuf_delete(buf); + } + + printf("\n[End transmission]\n"); + + netconn_disconnect(conn); + + for(;;) + asm volatile("hlt"); +} /* * Network-specific initialization @@ -1005,6 +1067,7 @@ static void network_init(void) { struct bootp_t *bp = (struct bootp_t *)trackbuf; int pkt_len; + int i; *LocalDomain = 0; /* No LocalDomain received */ @@ -1060,13 +1123,28 @@ static void network_init(void) if ((DHCPMagic & 1) == 0) DHCPMagic = 0; -#if 1 +#if 1 /* new stuff */ + dprintf("undi_tcpip_start...\n"); extern err_t undi_tcpip_start(struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw); undi_tcpip_start((struct ip_addr *)&IPInfo.myip, (struct ip_addr *)&IPInfo.netmask, (struct ip_addr *)&IPInfo.gateway); + + for (i = 0; i < DNS_MAX_SERVERS; i++) { + /* Transfer the DNS information to lwip */ + dprintf("DNS server %d = %d.%d.%d.%d\n", + i, + ((uint8_t *)&dns_server[i])[0], + ((uint8_t *)&dns_server[i])[1], + ((uint8_t *)&dns_server[i])[2], + ((uint8_t *)&dns_server[i])[3]); + dns_setserver(i, (struct ip_addr *)&dns_server[i]); + } + + dprintf("lwip_test...\n"); + lwip_test(); #else udp_init(); #endif -- cgit v1.2.1 From 5676daf48c35a13e051284814d936822c5e42662 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 16 Sep 2009 19:56:39 -0700 Subject: lwip: better test case Download a large file from www3.kernel.org (Amsterdam) and time the result. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 14a767d5..8410bf56 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1007,8 +1007,12 @@ static void lwip_test(void) struct netconn *conn; char header_buf[512]; int header_len; - static const char host_str[] = "www.zytor.com"; + static const char host_str[] = "www3.kernel.org"; struct netbuf *buf; + mstime_t t0, t1; + size_t bytes, x_bytes; + bool found_eoh; + int found_nl; /* Test the lwIP stack by trying to open a HTTP connection... */ printf("Starting lwIP test...\n"); @@ -1027,13 +1031,18 @@ static void lwip_test(void) printf("netconn_connect error %d\n", err); header_len = snprintf(header_buf, sizeof header_buf, - "GET /lwip_test HTTP/1.0\r\n" + "GET /pub/linux/kernel/v2.6/linux-2.6.31.tar.gz HTTP/1.0\r\n" "Host: %s\r\n" "\r\n", host_str); err = netconn_write(conn, header_buf, header_len, NETCONN_NOCOPY); printf("netconn_write error %d\n", err); + bytes = x_bytes = 0; + found_nl = 0; + found_eoh = false; + + t0 = ms_timer(); for (;;) { void *data; char *p; @@ -1043,16 +1052,37 @@ static void lwip_test(void) if (!buf) break; - netbuf_data(buf, &data, &len); - - p = data; - while (len--) - putchar (*p++); + do { + netbuf_data(buf, &data, &len); + p = data; + while (__unlikely(!found_eoh && len)) { + printf("%c", *p); + switch (*p) { + case '\r': + break; + case '\n': + if (++found_nl == 2) + found_eoh = true; + break; + default: + found_nl = 0; + break; + } + p++; + len--; + } + bytes += len; + if ((bytes^x_bytes) >> 20) { + printf("%dM\r", bytes >> 20); + x_bytes = bytes; + } + } while (netbuf_next(buf) >= 0); netbuf_delete(buf); } - - printf("\n[End transmission]\n"); + t1 = ms_timer(); + + printf("Done: %zu bytes in %u ms\n", bytes, (t1-t0)); netconn_disconnect(conn); -- cgit v1.2.1 From 7bffc3a00ee5b491ec27242bdbe3afba1c532eee Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 17 Sep 2009 17:06:34 -0700 Subject: core: lwip: tune parameters for performance With these tweaks, we are up from 6 Mbps to over 200 Mbps on a back-to-back gigabit TCP connection. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 07990615..a59b91a9 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -91,6 +91,12 @@ static void pxe_receive_thread(void *dummy) void pxe_init_isr(void) { start_idle_thread(); - start_thread("pxe receive", 16384, 0, pxe_receive_thread, NULL); + /* + * 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. + */ + start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); core_pm_hook = pm_return; } -- cgit v1.2.1 From 8ee12e55b21202c22f7cd3a0453024be98b727fd Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 17 Sep 2009 17:32:21 -0700 Subject: lwip: better speed test Better speed test, to make tuning easier. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 125 +++++++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 58 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 8410bf56..5a249179 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1011,8 +1011,10 @@ static void lwip_test(void) struct netbuf *buf; mstime_t t0, t1; size_t bytes, x_bytes; + size_t ms, kbits_per_sec; bool found_eoh; int found_nl; + int i; /* Test the lwIP stack by trying to open a HTTP connection... */ printf("Starting lwIP test...\n"); @@ -1024,67 +1026,74 @@ static void lwip_test(void) ((uint8_t *)&ip)[3], err); - conn = netconn_new(NETCONN_TCP); - printf("netconn_new returned %p\n", conn); - - err = netconn_connect(conn, &ip, 80); - printf("netconn_connect error %d\n", err); - - header_len = snprintf(header_buf, sizeof header_buf, - "GET /pub/linux/kernel/v2.6/linux-2.6.31.tar.gz HTTP/1.0\r\n" - "Host: %s\r\n" - "\r\n", - host_str); - - err = netconn_write(conn, header_buf, header_len, NETCONN_NOCOPY); - printf("netconn_write error %d\n", err); - bytes = x_bytes = 0; - found_nl = 0; - found_eoh = false; - - t0 = ms_timer(); - for (;;) { - void *data; - char *p; - u16_t len; - - buf = netconn_recv(conn); - if (!buf) - break; - - do { - netbuf_data(buf, &data, &len); - p = data; - while (__unlikely(!found_eoh && len)) { - printf("%c", *p); - switch (*p) { - case '\r': - break; - case '\n': - if (++found_nl == 2) - found_eoh = true; - break; - default: - found_nl = 0; + for (i = 1; i < 20; i++) { + conn = netconn_new(NETCONN_TCP); + err = netconn_connect(conn, &ip, 80); + if (err) { + printf("netconn_connect error %d\n", err); + continue; + } + + header_len = snprintf(header_buf, sizeof header_buf, + "GET /pub/linux/kernel/v2.6/linux-2.6.31.tar.gz HTTP/1.0\r\n" + "Host: %s\r\n" + "\r\n", + host_str); + + err = netconn_write(conn, header_buf, header_len, NETCONN_NOCOPY); + if (err) + printf("netconn_write error %d\n", err); + bytes = x_bytes = 0; + found_nl = 0; + found_eoh = false; + + t0 = ms_timer(); + for (;;) { + void *data; + char *p; + u16_t len; + + buf = netconn_recv(conn); + if (!buf) break; - } - p++; - len--; - } - bytes += len; - if ((bytes^x_bytes) >> 20) { - printf("%dM\r", bytes >> 20); - x_bytes = bytes; - } - } while (netbuf_next(buf) >= 0); - - netbuf_delete(buf); - } - t1 = ms_timer(); + + do { + netbuf_data(buf, &data, &len); + p = data; + while (__unlikely(!found_eoh && len)) { + switch (*p) { + case '\r': + break; + case '\n': + if (++found_nl == 2) + found_eoh = true; + break; + default: + found_nl = 0; + break; + } + p++; + len--; + } + bytes += len; + if ((bytes^x_bytes) >> 20) { + printf("%dM\r", bytes >> 20); + x_bytes = bytes; + } + } while (netbuf_next(buf) >= 0); + + netbuf_delete(buf); + } + t1 = ms_timer(); + ms = t1 - t0; - printf("Done: %zu bytes in %u ms\n", bytes, (t1-t0)); + kbits_per_sec = (bytes << 3) / ms; - netconn_disconnect(conn); + printf("Done: %zu bytes in %u ms (%u.%03u Mbps)\n", + bytes, ms, kbits_per_sec/1000, kbits_per_sec%1000); + + netconn_disconnect(conn); + } for(;;) asm volatile("hlt"); -- cgit v1.2.1 From f41a7c9225eb32379ef401a0ff419b75969e0f69 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 18 Sep 2009 13:56:53 -0700 Subject: lwip: show stats when displaying speedtest results Performance seems to be heavily inversely correlated with packet loss... Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 5a249179..3f84ea54 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1000,6 +1000,24 @@ static void udp_init(void) } #endif +static void undi_clear_stats(void) +{ + static __lowmem t_PXENV_UNDI_CLEAR_STATISTICS clear; + + pxe_call(PXENV_UNDI_CLEAR_STATISTICS, &clear); +} + +static void undi_stats(void) +{ + static __lowmem t_PXENV_UNDI_GET_STATISTICS stats; + + pxe_call(PXENV_UNDI_GET_STATISTICS, &stats); + + printf("UNDI: Xmit %u Rcv %u CRC %u Resource %u\n", + stats.XmtGoodFrames, stats.RcvGoodFrames, + stats.RcvCRCErrors, stats.RcvResourceErrors); +} + static void lwip_test(void) { err_t err; @@ -1008,6 +1026,7 @@ static void lwip_test(void) char header_buf[512]; int header_len; static const char host_str[] = "www3.kernel.org"; + static const char path_str[] = "/pub/linux/kernel/v2.6/linux-2.6.31.tar.gz"; struct netbuf *buf; mstime_t t0, t1; size_t bytes, x_bytes; @@ -1026,7 +1045,9 @@ static void lwip_test(void) ((uint8_t *)&ip)[3], err); - for (i = 1; i < 20; i++) { + for (i = 1; i < 10; i++) { + undi_clear_stats(); + conn = netconn_new(NETCONN_TCP); err = netconn_connect(conn, &ip, 80); if (err) { @@ -1035,10 +1056,10 @@ static void lwip_test(void) } header_len = snprintf(header_buf, sizeof header_buf, - "GET /pub/linux/kernel/v2.6/linux-2.6.31.tar.gz HTTP/1.0\r\n" + "GET %s HTTP/1.0\r\n" "Host: %s\r\n" "\r\n", - host_str); + path_str, host_str); err = netconn_write(conn, header_buf, header_len, NETCONN_NOCOPY); if (err) @@ -1091,6 +1112,7 @@ static void lwip_test(void) printf("Done: %zu bytes in %u ms (%u.%03u Mbps)\n", bytes, ms, kbits_per_sec/1000, kbits_per_sec%1000); + undi_stats(); netconn_disconnect(conn); } -- cgit v1.2.1 From 3d35705a06f04b3e08f701278cec648336233562 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 9 Apr 2011 21:24:29 -0700 Subject: core pxe: Cleanup the call to pxe_isr_init Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 2 +- core/fs/pxe/pxe.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 3f84ea54..af580c63 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1217,9 +1217,9 @@ static void network_init(void) */ static int pxe_fs_init(struct fs_info *fs) { - extern void pxe_init_isr(void); /* XXX */ (void)fs; /* drop the compile warning message */ + /* Prepare for handling pxe interrupts */ pxe_init_isr(); /* This block size is actually arbitrary... */ diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 60feedb7..45e5008e 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -232,6 +232,9 @@ static inline uint32_t gateway(uint32_t ip) * functions */ +/* isr.c */ +void pxe_init_isr(void); + /* pxe.c */ bool ip_ok(uint32_t); int pxe_call(int, void *); -- cgit v1.2.1 From 71713e663a99015cccddd2662ce97233f54fa368 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 10 Apr 2011 05:14:49 -0700 Subject: pxe: Send tftp acks after we reach our timeout While looking at the tftp implementation I noticed that we are not retransmitting acks if we reach our timeout. Not retransmitting can be a problem when there is packet loss present in a network. We do retransmit if the server times out but it is better if we retransmit ourselves in case we are dealing with a stupid tftp server that has not noticed it missed a packet from us. Signed-off-by: Eric W. Biederman --- core/fs/pxe/tftp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'core/fs') diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 4fbe4058..0f1fa56c 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -141,6 +141,7 @@ static void tftp_get_packet(struct inode *inode) timeout = *timeout_ptr++; if (!timeout) break; + goto ack_again; } continue; } -- cgit v1.2.1 From 0290be0cbaae619892c91def3cd13c54614a9dbb Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 10 Apr 2011 05:29:12 -0700 Subject: core pxe: Use the lwip dns resolver When we start receiving packets at the undi layer we can no longer use the the pxe udp layer. So make our lives simple by using the lwip dns resolve which which works without changes and seems to be a little more comprehensive. Signed-off-by: Eric W. Biederman --- core/fs/pxe/dnsresolv.c | 252 ++++-------------------------------------------- 1 file changed, 19 insertions(+), 233 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c index 06d5a73f..b827edbb 100644 --- a/core/fs/pxe/dnsresolv.c +++ b/core/fs/pxe/dnsresolv.c @@ -2,6 +2,8 @@ #include #include #include "pxe.h" +#include "lwip/api.h" +#include "lwip/dns.h" /* DNS CLASS values we care about */ #define CLASS_IN 1 @@ -86,255 +88,39 @@ int dns_mangle(char **dst, const char *p) return dots; } - -/* - * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_ - * is allowed pointers relative to a packet in buf. - * - */ -static bool dns_compare(const void *s1, const void *s2, const void *buf) -{ - const uint8_t *q = s1; - const uint8_t *p = s2; - unsigned int c0, c1; - - while (1) { - c0 = p[0]; - if (c0 >= 0xc0) { - /* Follow pointer */ - c1 = p[1]; - p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1; - } else if (c0) { - c0++; /* Include the length byte */ - if (memcmp(q, p, c0)) - return false; - q += c0; - p += c0; - } else { - return *q == 0; - } - } -} - -/* - * Copy a DNS label into a buffer, considering the possibility that we might - * have to follow pointers relative to "buf". - * Returns a pointer to the first free byte *after* the terminal null. - */ -static void *dns_copylabel(void *dst, const void *src, const void *buf) -{ - uint8_t *q = dst; - const uint8_t *p = src; - unsigned int c0, c1; - - while (1) { - c0 = p[0]; - if (c0 >= 0xc0) { - /* Follow pointer */ - c1 = p[1]; - p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1; - } else if (c0) { - c0++; /* Include the length byte */ - memcpy(q, p, c0); - p += c0; - q += c0; - } else { - *q++ = 0; - return q; - } - } -} - -/* - * Skip past a DNS label set in DS:SI - */ -static char *dns_skiplabel(char *label) -{ - uint8_t c; - - while (1) { - c = *label++; - if (c >= 0xc0) - return ++label; /* pointer is two bytes */ - if (c == 0) - return label; - label += c; - } -} - /* * Actual resolver function * Points to a null-terminated or :-terminated string in _name_ * and returns the ip addr in _ip_ if it exists and can be found. * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated - * - * XXX: probably need some caching here. */ 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; - jiffies_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; + err_t err; + struct ip_addr ip; + char dns_name[PKTBUF_SIZE]; + const char *src; + char *dst; /* Make sure we have at least one valid DNS server */ - if (!dns_server[0]) + if (!dns_getserver(0).addr) 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++; + /* Copy the name to look up to ensure it is null terminated */ + for (dst = dns_name, src = name; *src; src++, dst++) { + int ch = *src; + if (ch == '\0' || ch == ':' || ch == '/') { + *dst = '\0'; + break; } - - udp_write.status = 0; - udp_write.ip = srv; - udp_write.gw = gateway(srv); - udp_write.src_port = local_port; - udp_write.dst_port = DNS_PORT; - udp_write.buffer_size = p - DNSSendBuf; - udp_write.buffer = FAR_PTR(DNSSendBuf); - err = pxe_call(PXENV_UDP_WRITE, &udp_write); - if (err || udp_write.status) - continue; - - oldtime = jiffies(); - do { - if (jiffies() - oldtime >= timeout) - goto again; - - udp_read.status = 0; - udp_read.src_ip = srv; - udp_read.dest_ip = IPInfo.myip; - udp_read.s_port = DNS_PORT; - udp_read.d_port = local_port; - udp_read.buffer_size = PKTBUF_SIZE; - udp_read.buffer = FAR_PTR(DNSRecvBuf); - err = pxe_call(PXENV_UDP_READ, &udp_read); - } while (err || udp_read.status || hd2->id != hd1->id); - - if ((hd2->flags ^ 0x80) & htons(0xf80f)) - goto badness; - - ques = htons(hd2->qdcount); /* Questions */ - reps = htons(hd2->ancount); /* Replies */ - p = DNSRecvBuf + sizeof(struct dnshdr); - while (ques--) { - p = dns_skiplabel(p); /* Skip name */ - p += 4; /* Skip question trailer */ - } - - /* Parse the replies */ - while (reps--) { - same = dns_compare(DNSSendBuf + sizeof(struct dnshdr), - p, DNSRecvBuf); - p = dns_skiplabel(p); - rr = (struct dnsrr *)p; - rd_len = ntohs(rr->rdlength); - if (same && ntohs(rr->class) == CLASS_IN) { - switch (ntohs(rr->type)) { - case TYPE_A: - if (rd_len == 4) { - result = *(uint32_t *)rr->rdata; - goto done; - } - break; - case TYPE_CNAME: - dns_copylabel(DNSSendBuf + sizeof(struct dnshdr), - rr->rdata, DNSRecvBuf); - /* - * We should probably rescan the packet from the top - * here, and technically we might have to send a whole - * new request here... - */ - break; - default: - break; - } - } - - /* not the one we want, try next */ - p += sizeof(struct dnsrr) + rd_len; - } - - badness: - /* - * - ; We got back no data from this server. - ; Unfortunately, for a recursive, non-authoritative - ; query there is no such thing as an NXDOMAIN reply, - ; which technically means we can't draw any - ; conclusions. However, in practice that means the - ; domain doesn't exist. If this turns out to be a - ; problem, we may want to add code to go through all - ; the servers before giving up. - - ; If the DNS server wasn't capable of recursion, and - ; isn't capable of giving us an authoritative reply - ; (i.e. neither AA or RA set), then at least try a - ; different setver... - */ - if (hd2->flags == htons(0x480)) - continue; - - break; /* failed */ - - again: - continue; + *dst = ch; } -done: - free_port(local_port); /* Return port number to the free pool */ + err = netconn_gethostbyname(dns_name, &ip); + if (err) + return 0; - return result; + return ip.addr; } -- cgit v1.2.1 From e0428a557da2ec94363dd5f8be842df198c656c3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 10 Apr 2011 05:31:38 -0700 Subject: core: pxe: Add a netconn socket to the pxe private inode Now that we are using lwip, all implementations of transfer protocols will need a netconn, so add it to the pxe inode. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 45e5008e..274f1c9f 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -149,10 +149,12 @@ struct bootp_t { uint8_t options[1260]; /* Vendor options */ } __attribute__ ((packed)); +struct netconn; /* * Our inode private information -- this includes the packet buffer! */ struct pxe_pvt_inode { + struct netconn *conn; /* lwip network connection */ 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 */ -- cgit v1.2.1 From 736ce0f3ee36382b29bb722a86e22289924623b3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 10 Apr 2011 05:34:31 -0700 Subject: core: pxe: Rewrite the tftp implementation to use lwip Since the raw pxe udp interface is unusable when lwip is active rewrite the tftp implmentation to use netconn. I expect this implementation can be simplified but my goal was as straight forward a transformation as I could possibly mange to avoid bugs slipping in. Signed-off-by: Eric W. Biederman --- core/fs/pxe/tftp.c | 137 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 59 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 0f1fa56c..4f21a487 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -1,5 +1,6 @@ #include #include "pxe.h" +#include "lwip/api.h" const uint8_t TimeoutTable[] = { 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44, @@ -30,6 +31,10 @@ static void tftp_close_file(struct inode *inode) if (socket->tftp_localport != 0) { tftp_error(inode, 0, "No error, file close"); } + if (socket->conn) { + netconn_delete(socket->conn); + socket->conn = NULL; + } } /** @@ -42,12 +47,12 @@ static void tftp_close_file(struct inode *inode) static void tftp_error(struct inode *inode, uint16_t errnum, const char *errstr) { - static __lowmem struct { + static 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; + struct netbuf *nbuf; int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1); struct pxe_pvt_inode *socket = PVT(inode); @@ -56,15 +61,11 @@ static void tftp_error(struct inode *inode, uint16_t 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; - + nbuf = netbuf_new(); + netbuf_ref(nbuf, &err_buf, 4 + len + 1); + netconn_send(socket->conn, nbuf); /* If something goes wrong, there is nothing we can do, anyway... */ - pxe_call(PXENV_UDP_WRITE, &udp_write); + netbuf_delete(nbuf); } /** @@ -76,22 +77,19 @@ static void tftp_error(struct inode *inode, uint16_t errnum, */ 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; + err_t err; + static uint16_t ack_packet_buf[2]; + struct netbuf *nbuf; 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); + + nbuf = netbuf_new(); + netbuf_ref(nbuf, ack_packet_buf, 4); + err = netconn_send(socket->conn, nbuf); + netbuf_delete(nbuf); (void)err; #if 0 printf("sent %s\n", err ? "FAILED" : "OK"); @@ -104,14 +102,14 @@ static void ack_packet(struct inode *inode, uint16_t ack_num) */ static void tftp_get_packet(struct inode *inode) { - int err; int last_pkt; const uint8_t *timeout_ptr; uint8_t timeout; uint16_t buffersize; jiffies_t oldtime; void *data = NULL; - static __lowmem struct s_PXENV_UDP_READ udp_read; + struct netbuf *nbuf; + u16_t nbuf_len; struct pxe_pvt_inode *socket = PVT(inode); /* @@ -126,14 +124,8 @@ static void tftp_get_packet(struct inode *inode) 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) { + nbuf = netconn_recv(socket->conn); + if (!nbuf) { jiffies_t now = jiffies(); if (now-oldtime >= timeout) { @@ -144,10 +136,18 @@ static void tftp_get_packet(struct inode *inode) goto ack_again; } continue; - } + } - if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */ - continue; + netbuf_first(nbuf); + nbuf_len = 0; + nbuf_len = netbuf_len(nbuf); + if (nbuf_len <= PKTBUF_SIZE) + netbuf_copy(nbuf, packet_buf, nbuf_len); + else + nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ + netbuf_delete(nbuf); + if (nbuf_len < 4) /* Bad size for a DATA packet */ + continue; data = packet_buf; if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */ @@ -180,7 +180,7 @@ static void tftp_get_packet(struct inode *inode) /* It's the packet we want. We're also EOF if the size < blocksize */ socket->tftp_lastpkt = last_pkt; /* Update last packet number */ - buffersize = udp_read.buffer_size - 4; /* Skip TFTP header */ + buffersize = nbuf_len - 4; /* Skip TFTP header */ memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize); socket->tftp_dataptr = socket->tftp_pktbuf; socket->tftp_filepos += buffersize; @@ -192,6 +192,7 @@ static void tftp_get_packet(struct inode *inode) /* Make sure we know we are at end of file */ inode->size = socket->tftp_filepos; socket->tftp_goteof = 1; + tftp_close_file(inode); } } @@ -211,11 +212,11 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, { struct pxe_pvt_inode *socket = PVT(inode); char *buf; + struct netbuf *nbuf; + u16_t nbuf_len; 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 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; @@ -230,10 +231,22 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, uint16_t opcode; uint16_t blk_num; uint32_t opdata, *opdata_ptr; + struct ip_addr addr; socket->fill_buffer = tftp_get_packet; socket->close = tftp_close_file; + socket->conn = netconn_new(NETCONN_UDP); + if (!socket->conn) + return; + + socket->conn->recv_timeout = 15; /* A 15 ms recv timeout... */ + err = netconn_bind(socket->conn, NULL, ntohs(socket->tftp_localport)); + if (err) { + printf("netconn_bind error %d\n", err); + return; + } + buf = rrq_packet_buf; *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ buf += 2; @@ -249,6 +262,7 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, timeout_ptr = TimeoutTable; /* Reset timeout */ sendreq: + netconn_disconnect(socket->conn); timeout = *timeout_ptr++; if (!timeout) return; /* No file available... */ @@ -256,43 +270,44 @@ sendreq: 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); + nbuf = netbuf_new(); + netbuf_ref(nbuf, rrq_packet_buf, rrq_len); + addr.addr = ip; + netconn_sendto(socket->conn, nbuf, &addr, ntohs(server_port)); + netbuf_delete(nbuf); /* If the WRITE call fails, we let the timeout take care of it... */ - wait_pkt: + netconn_disconnect(socket->conn); 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) { + nbuf = netconn_recv(socket->conn); + if (!nbuf) { jiffies_t now = jiffies(); if (now - oldtime >= timeout) - goto sendreq; - } else { + goto sendreq; + } else { /* Make sure the packet actually came from the server */ - if (udp_read.src_ip == socket->tftp_remoteip) - break; + bool ok_source; + ok_source = netbuf_fromaddr(nbuf)->addr == socket->tftp_remoteip; + nbuf_len = netbuf_len(nbuf); + if (nbuf_len <= PKTBUF_SIZE) + netbuf_copy(nbuf, packet_buf, nbuf_len); + else + nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ + netbuf_delete(nbuf); + if (ok_source) + break; } } - socket->tftp_remoteport = udp_read.s_port; + socket->tftp_remoteport = htons(netbuf_fromport(nbuf)); + netconn_connect(socket->conn, netbuf_fromaddr(nbuf), netbuf_fromport(nbuf)); /* 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 */ + buffersize = nbuf_len - 2; /* bytes after opcode */ if (buffersize < 0) goto wait_pkt; /* Garbled reply */ @@ -419,6 +434,10 @@ wait_pkt: goto err_reply; } done: + if (!inode->size) { + netconn_delete(socket->conn); + socket->conn = NULL; + } return; err_reply: -- cgit v1.2.1 From d87ae0022e613ecee4f2627c949cd6f67c980ba7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 10 Apr 2011 06:14:17 -0700 Subject: core: pxe: Factor out pxe_poll from pxe_receive_thread Signed-off-by: Eric W. Biederman --- core/fs/pxe/isr.c | 78 ++++++++++++++++++++++++++++--------------------------- core/fs/pxe/pxe.h | 1 + 2 files changed, 41 insertions(+), 38 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index a59b91a9..4eac5aaf 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -40,54 +40,56 @@ static void pm_return(void) void undiif_input(t_PXENV_UNDI_ISR *); -static void pxe_receive_thread(void *dummy) +void pxe_poll(void) { static __lowmem t_PXENV_UNDI_ISR isr; - uint16_t func; - bool done; + 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); - func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */ - - 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; - } - } + pxe_poll(); } } - void pxe_init_isr(void) { start_idle_thread(); diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 274f1c9f..dd1d31f0 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -236,6 +236,7 @@ static inline uint32_t gateway(uint32_t ip) /* isr.c */ void pxe_init_isr(void); +void pxe_poll(void); /* pxe.c */ bool ip_ok(uint32_t); -- cgit v1.2.1 From f7af71ef607670b22ce52d314bc38e1f39d32431 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 11 Apr 2011 04:16:03 -0700 Subject: core: pxe: Add general support for pluggable url handlers. In preparation for adding http support (and possibly others) add support for looking up a url scheme by name in a table and calling the appropriate open function. This also adds a struct netbuf pointer to the pxe inode allowing for some easy to implement and nice to use streaming abstractions like pxe_getc. Cleanup the includes of lwip headers. We want <> brackets (no point in searching in the current directory first) and the ifdefs around them can go. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++----- core/fs/pxe/pxe.h | 4 ++++ 2 files changed, 53 insertions(+), 5 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index af580c63..c173cd0a 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -6,10 +6,8 @@ #include #include #include "pxe.h" -#if 1 -#include "lwip/api.h" -#include "lwip/dns.h" -#endif +#include +#include static uint16_t real_base_mem; /* Amount of DOS memory after freeing */ @@ -33,6 +31,13 @@ bool have_uuid = false; /* Common receive buffer */ __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); +static struct url_open { + const char *scheme; + void (*open)(struct inode *inode, const char *url); +} url_table[] = { + { NULL, NULL }, +}; + /* * Allocate a local UDP port structure and assign it a local port number. * Return the inode pointer if success, or null if failure @@ -184,7 +189,7 @@ static int gendotquad(char *dst, uint32_t ip) * return the the string address after the ip string * */ -static const char *parse_dotquad(const char *ip_str, uint32_t *res) +const char *parse_dotquad(const char *ip_str, uint32_t *res) { const char *p = ip_str; uint8_t part = 0; @@ -321,6 +326,30 @@ static void pxe_mangle_name(char *dst, const char *src) *dst = '\0'; } +/* + * Read a single character from the specified pxe inode. + * Very useful for stepping through http streams and + * parsing their headers. + */ +int pxe_getc(struct inode *inode) +{ + struct pxe_pvt_inode *socket = PVT(inode); + unsigned char byte; + + while (!socket->tftp_bytesleft) { + if (socket->tftp_goteof) + return -1; + + socket->fill_buffer(inode); + } + + byte = *socket->tftp_dataptr; + socket->tftp_bytesleft -= 1; + socket->tftp_dataptr += 1; + + return byte; +} + /* * Get a fresh packet if the buffer is drained, and we haven't hit * EOF yet. The buffer should be filled immediately after draining! @@ -487,6 +516,21 @@ static void __pxe_searchdir(const char *filename, struct file *file) if (!inode) return; /* Allocation failure */ + if (path_type == PXE_URL) { + struct url_open *entry; + np = strchr(filename, ':'); + + for (entry = url_table; entry->scheme; entry++) { + int scheme_len = strlen(entry->scheme); + if (scheme_len != (np - filename)) + continue; + if (memcmp(entry->scheme, filename, scheme_len) != 0) + continue; + entry->open(inode, filename); + goto done; + } + } + #if GPXE if (path_type == PXE_URL) { if (has_gpxe) { diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index dd1d31f0..59661bcb 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -150,11 +150,13 @@ struct bootp_t { } __attribute__ ((packed)); struct netconn; +struct netbuf; /* * Our inode private information -- this includes the packet buffer! */ struct pxe_pvt_inode { struct netconn *conn; /* lwip network connection */ + struct netbuf *buf; /* lwip cached buffer */ 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 */ @@ -242,6 +244,8 @@ void pxe_poll(void); bool ip_ok(uint32_t); int pxe_call(int, void *); extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); +const char *parse_dotquad(const char *ip_str, uint32_t *res); +int pxe_getc(struct inode *inode); /* dhcp_options.c */ void parse_dhcp(int); -- cgit v1.2.1 From 4992782a6f87f02f47306a06249d94c93154b4cd Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 11 Apr 2011 04:22:57 -0700 Subject: core: pxe Add native support for http urls Signed-off-by: Eric W. Biederman --- core/fs/pxe/http.c | 375 +++++++++++++++++++++++++++++++++++++++++++++++++++++ core/fs/pxe/pxe.c | 1 + core/fs/pxe/pxe.h | 3 + 3 files changed, 379 insertions(+) create mode 100644 core/fs/pxe/http.c (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c new file mode 100644 index 00000000..6af55d59 --- /dev/null +++ b/core/fs/pxe/http.c @@ -0,0 +1,375 @@ +#include +#include "pxe.h" +#include "../../../version.h" +#include + +static void http_close_file(struct inode *inode) +{ + struct pxe_pvt_inode *socket = PVT(inode); + if (socket->buf) { + netbuf_delete(socket->buf); + socket->buf = NULL; + } + if (socket->conn) { + netconn_delete(socket->conn); + socket->conn = NULL; + } +} + +static void http_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->buf) { + if (netbuf_next(socket->buf) < 0) { + netbuf_delete(socket->buf); + socket->buf = NULL; + } + } + /* If needed get a new netbuf */ + if (!socket->buf) { + socket->buf = netconn_recv(socket->conn); + if (!socket->buf) { + socket->tftp_goteof = 1; + if (inode->size == -1) + inode->size = socket->tftp_filepos; + http_close_file(inode); + return; + } + } + /* Report the current fragment of the netbuf */ + err = netbuf_data(socket->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; +} + +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; +} + +void http_open(struct inode *inode, const char *url) +{ + struct pxe_pvt_inode *socket = PVT(inode); + char header_buf[512]; + int header_len; + const char *host, *path, *next; + size_t host_len; + uint16_t port; + char field_name[20]; + char field_value[256]; + 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; + char location[256]; + uint32_t content_length; /* same as inode->size */ + size_t response_size; + int status; + int pos; + int redirect_count; + + socket->fill_buffer = http_fill_buffer; + socket->close = http_close_file; + redirect_count = 0; + +restart: + /* Reset all of the variables */ + inode->size = content_length = -1; + location[0] = '\0'; + field_name[0] = '\0'; + field_value[0] = '\0'; + field_name_len = 0; + field_value_len = 0; + + /* Skip http:// */ + host = url + 7; + + /* Find the end of the hostname */ + next = host; + while (*next && *next != '/' && *next != ':') + next++; + host_len = next - host; + + /* Obvious url formatting errors */ + if (!*next || (!host_len && *next == ':')) + goto fail; + + /* Compute the dest port */ + port = 80; + if (*next == ':') { + port = 0; + for (next++; (*next >= '0' && *next <= '9'); next++) + port = (port * 10) * (*next - '0'); + } + + /* Ensure I have properly parsed the port */ + if (*next != '/') + goto fail; + + path = next; + + /* Resolve the hostname */ + if (!host_len) { + addr.addr = IPInfo.serverip; + } else { + if (parse_dotquad(host, &addr.addr) != (host + host_len)) { + addr.addr = dns_resolv(host); + if (!addr.addr) + goto fail; + } + } + + /* Start the http connection */ + socket->conn = netconn_new(NETCONN_TCP); + if (!socket->conn) { + printf("netconn_new failed\n"); + return; + } + + err = netconn_connect(socket->conn, &addr, port); + if (err) { + printf("netconn_connect error %d\n", err); + goto fail; + } + + header_len = snprintf(header_buf, sizeof header_buf, + "GET %s HTTP/1.0\r\n" + "Host: %*.*s\r\n" + "User-Agent: PXELINUX/%s\r\n" + "Connection: close\r\n" + "\r\n", + path, host_len, host_len, host, VERSION_STR); + + /* If we tried to overflow our buffer abort */ + if (header_len > sizeof header_buf) + goto fail; + + err = netconn_write(socket->conn, header_buf, header_len, 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; + + 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++; + strcpy(location, next); + } + /* 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; + redirect_count++; + if (redirect_count > 5) + goto fail; + url = location; + goto restart; + break; + default: + goto fail; + break; + } + return; +fail: + inode->size = 0; + http_close_file(inode); + return; +} diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index c173cd0a..96c6e7b5 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -35,6 +35,7 @@ static struct url_open { const char *scheme; void (*open)(struct inode *inode, const char *url); } url_table[] = { + { "http", http_open }, { NULL, NULL }, }; diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 59661bcb..f41bf828 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -270,4 +270,7 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, void gpxe_open(struct inode *inode, const char *url); #define GPXE 1 +/* http.c */ +void http_open(struct inode *inode, const char *url); + #endif /* pxe.h */ -- cgit v1.2.1 From abf7a9d75878f7eac2fc363d934fce52fa33f532 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 11 Apr 2011 22:47:31 -0700 Subject: core: pxe: Improve the situation with installing and uninstalling irq handlers Signed-off-by: Eric W. Biederman --- core/fs/pxe/isr.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- core/fs/pxe/pxe.h | 8 ++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 4eac5aaf..4dcadc18 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -12,6 +12,46 @@ extern uint8_t pxe_irq_pending; static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); +static struct thread *pxe_thread; + +bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) +{ + far_ptr_t *entry; + unsigned int vec; + + if (irq < 8) + vec = irq + 0x08; + else if (irq < 16) + vec = (irq - 8) + 0x70; + else + return false; + + entry = (far_ptr_t *)(vec << 2); + *old = *entry; + entry->ptr = (uint32_t)isr; + return true; +} + +bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) +{ + far_ptr_t *entry; + unsigned int vec; + + if (irq < 8) + vec = irq + 0x08; + else if (irq < 16) + vec = (irq - 8) + 0x70; + else + return false; + + entry = (far_ptr_t *)(vec << 2); + + if (entry->ptr != (uint32_t)isr) + return false; + + *entry = *old; + return true; +} static void pm_return(void) { @@ -99,6 +139,20 @@ void pxe_init_isr(void) * avoid packet loss we need to move it into memory that we ourselves * manage, as soon as possible. */ - start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); + pxe_thread = start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); core_pm_hook = pm_return; } + + +void pxe_cleanup_isr(void) +{ + static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; + int err; + + core_pm_hook = core_pm_null_hook; + kill_thread(pxe_thread); + + memset(&undi_close, 0, sizeof(undi_close)); + err = pxe_call(PXENV_UNDI_CLOSE, &undi_close); + uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); +} diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index f41bf828..810646ea 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -236,9 +236,17 @@ static inline uint32_t gateway(uint32_t ip) * functions */ +/* pxeisr.inc */ +extern uint8_t pxe_irq_vector; +extern void pxe_isr(void); +extern far_ptr_t pxe_irq_chain; + /* isr.c */ void pxe_init_isr(void); +void pxe_cleanup_isr(void); void pxe_poll(void); +bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old); +bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old); /* pxe.c */ bool ip_ok(uint32_t); -- cgit v1.2.1 From 787ca77736848e5d3967e5823d009cfe1ddcb833 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 11 Apr 2011 23:02:11 -0700 Subject: core: pxe: Cleanup properly when using undi. Unhook the interrupt handler and stop the undi processing to make it save to exit, and allows unload_pxe to start succeeding again. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 96c6e7b5..50f205f7 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1420,6 +1420,8 @@ void unload_pxe(void) uint16_t Status; /* All calls have this as the first member */ } unload_call; + pxe_cleanup_isr(); + dprintf("FBM before unload = %d\n", BIOS_fbm); err = reset_pxe(); @@ -1438,7 +1440,8 @@ void unload_pxe(void) 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); + dprintf("PXE unload API call %04x failed: 0x%x\n", + api, unload_call.Status); goto cant_free; } } -- cgit v1.2.1 From 7a4d7ed51f4440cbcf8dfa6a75f0c74f9bd12856 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 12 Apr 2011 00:05:58 -0700 Subject: lwip: Clean up the initialization sequence Implement an initialization sequence for lwip that is essentially sane, and remove the previous udp stack support. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 44 ++++++++++++-------------------------------- core/fs/pxe/pxe.h | 3 +++ 2 files changed, 15 insertions(+), 32 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 50f205f7..47f89762 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -8,6 +8,7 @@ #include "pxe.h" #include #include +#include static uint16_t real_base_mem; /* Amount of DOS memory after freeing */ @@ -1027,24 +1028,6 @@ static void gpxe_init(void) } #if 0 -/* - * 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(); - } -} -#endif - static void undi_clear_stats(void) { static __lowmem t_PXENV_UNDI_CLEAR_STATISTICS clear; @@ -1165,6 +1148,7 @@ static void lwip_test(void) for(;;) asm volatile("hlt"); } +#endif /* * Network-specific initialization @@ -1172,6 +1156,7 @@ static void lwip_test(void) static void network_init(void) { struct bootp_t *bp = (struct bootp_t *)trackbuf; + int err; int pkt_len; int i; @@ -1229,14 +1214,15 @@ static void network_init(void) if ((DHCPMagic & 1) == 0) DHCPMagic = 0; -#if 1 /* new stuff */ - dprintf("undi_tcpip_start...\n"); - extern err_t undi_tcpip_start(struct ip_addr *ipaddr, - struct ip_addr *netmask, - struct ip_addr *gw); - undi_tcpip_start((struct ip_addr *)&IPInfo.myip, - (struct ip_addr *)&IPInfo.netmask, - (struct ip_addr *)&IPInfo.gateway); + /* 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 */ @@ -1248,12 +1234,6 @@ static void network_init(void) ((uint8_t *)&dns_server[i])[3]); dns_setserver(i, (struct ip_addr *)&dns_server[i]); } - - dprintf("lwip_test...\n"); - lwip_test(); -#else - udp_init(); -#endif } /* diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 810646ea..9236b109 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -255,6 +255,9 @@ extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); const char *parse_dotquad(const char *ip_str, uint32_t *res); int pxe_getc(struct inode *inode); +/* undiif.c */ +int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw); + /* dhcp_options.c */ void parse_dhcp(int); -- cgit v1.2.1 From 2ce3f9aee490450bcc6a55eb79887778efdeba59 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 12 Apr 2011 00:14:12 -0700 Subject: pxe: Remove the lwip_test code lwip works so we no longer need this test code. Signed-off-by: Eric W. Biederman --- core/fs/pxe/pxe.c | 123 ------------------------------------------------------ 1 file changed, 123 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 47f89762..b9a7a04e 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1027,129 +1027,6 @@ static void gpxe_init(void) has_gpxe = (~gpxe_funcs & 0x4b) == 0; } -#if 0 -static void undi_clear_stats(void) -{ - static __lowmem t_PXENV_UNDI_CLEAR_STATISTICS clear; - - pxe_call(PXENV_UNDI_CLEAR_STATISTICS, &clear); -} - -static void undi_stats(void) -{ - static __lowmem t_PXENV_UNDI_GET_STATISTICS stats; - - pxe_call(PXENV_UNDI_GET_STATISTICS, &stats); - - printf("UNDI: Xmit %u Rcv %u CRC %u Resource %u\n", - stats.XmtGoodFrames, stats.RcvGoodFrames, - stats.RcvCRCErrors, stats.RcvResourceErrors); -} - -static void lwip_test(void) -{ - err_t err; - struct ip_addr ip; - struct netconn *conn; - char header_buf[512]; - int header_len; - static const char host_str[] = "www3.kernel.org"; - static const char path_str[] = "/pub/linux/kernel/v2.6/linux-2.6.31.tar.gz"; - struct netbuf *buf; - mstime_t t0, t1; - size_t bytes, x_bytes; - size_t ms, kbits_per_sec; - bool found_eoh; - int found_nl; - int i; - - /* Test the lwIP stack by trying to open a HTTP connection... */ - printf("Starting lwIP test...\n"); - err = netconn_gethostbyname(host_str, &ip); - printf("Gethostbyname: ip = %d.%d.%d.%d, err = %d\n", - ((uint8_t *)&ip)[0], - ((uint8_t *)&ip)[1], - ((uint8_t *)&ip)[2], - ((uint8_t *)&ip)[3], - err); - - for (i = 1; i < 10; i++) { - undi_clear_stats(); - - conn = netconn_new(NETCONN_TCP); - err = netconn_connect(conn, &ip, 80); - if (err) { - printf("netconn_connect error %d\n", err); - continue; - } - - header_len = snprintf(header_buf, sizeof header_buf, - "GET %s HTTP/1.0\r\n" - "Host: %s\r\n" - "\r\n", - path_str, host_str); - - err = netconn_write(conn, header_buf, header_len, NETCONN_NOCOPY); - if (err) - printf("netconn_write error %d\n", err); - bytes = x_bytes = 0; - found_nl = 0; - found_eoh = false; - - t0 = ms_timer(); - for (;;) { - void *data; - char *p; - u16_t len; - - buf = netconn_recv(conn); - if (!buf) - break; - - do { - netbuf_data(buf, &data, &len); - p = data; - while (__unlikely(!found_eoh && len)) { - switch (*p) { - case '\r': - break; - case '\n': - if (++found_nl == 2) - found_eoh = true; - break; - default: - found_nl = 0; - break; - } - p++; - len--; - } - bytes += len; - if ((bytes^x_bytes) >> 20) { - printf("%dM\r", bytes >> 20); - x_bytes = bytes; - } - } while (netbuf_next(buf) >= 0); - - netbuf_delete(buf); - } - t1 = ms_timer(); - ms = t1 - t0; - - kbits_per_sec = (bytes << 3) / ms; - - printf("Done: %zu bytes in %u ms (%u.%03u Mbps)\n", - bytes, ms, kbits_per_sec/1000, kbits_per_sec%1000); - undi_stats(); - - netconn_disconnect(conn); - } - - for(;;) - asm volatile("hlt"); -} -#endif - /* * Network-specific initialization */ -- cgit v1.2.1 From 6a54b8029280339e6b54c8c0f2780c74713cdffb Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 12 Apr 2011 04:38:21 -0700 Subject: pxe: Cleanup interrupt handling making it reliabe and in spec - Rework pm_return into pxe_poll_wakeups and use the new sched_hook_funk to call it from schedule. That is a little extra work but it is always correct to do. - Unconditionally call schedule from the pm_core_hook. schedule now does everything pm_return used to do if perhaps in a more braindead way so this is correct and safe. - Declare undiif_input in pxe.h - Stop exporting pxe_poll. Having it exported helped me track down what was going on but it was the wrong way to do things and exporting it is no longer needed. - Rename pxe_poll pxe_process_irq for clarity. Signed-off-by: Eric W. Biederman --- core/fs/pxe/isr.c | 27 +++++++++------------------ core/fs/pxe/pxe.h | 2 +- 2 files changed, 10 insertions(+), 19 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 4dcadc18..cb2300e0 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -53,34 +53,23 @@ bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) return true; } -static void pm_return(void) +static void pxe_poll_wakeups(void) { static jiffies_t last_jiffies = 0; jiffies_t now = jiffies(); - - __schedule_lock++; if (now != last_jiffies) { last_jiffies = now; __thread_process_timeouts(); - __need_schedule = true; /* Switch threads if more than one runnable */ } - + if (pxe_irq_pending) { pxe_irq_pending = 0; - if (pxe_receive_thread_sem.count <= 0) - sem_up(&pxe_receive_thread_sem); + sem_up(&pxe_receive_thread_sem); } - - __schedule_lock--; - - if (__need_schedule) - __schedule(); } -void undiif_input(t_PXENV_UNDI_ISR *); - -void pxe_poll(void) +static void pxe_process_irq(void) { static __lowmem t_PXENV_UNDI_ISR isr; @@ -104,7 +93,7 @@ void pxe_poll(void) break; case PXENV_UNDI_ISR_OUT_RECEIVE: - undiif_input(&isr); + undiif_input(&isr); break; case PXENV_UNDI_ISR_OUT_BUSY: @@ -126,13 +115,14 @@ static void pxe_receive_thread(void *dummy) for (;;) { sem_down(&pxe_receive_thread_sem, 0); - pxe_poll(); + pxe_process_irq(); } } 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 @@ -140,7 +130,7 @@ void pxe_init_isr(void) * manage, as soon as possible. */ pxe_thread = start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); - core_pm_hook = pm_return; + core_pm_hook = __schedule; } @@ -149,6 +139,7 @@ void pxe_cleanup_isr(void) static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; int err; + sched_hook_func = NULL; core_pm_hook = core_pm_null_hook; kill_thread(pxe_thread); diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 9236b109..d30e7839 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -244,7 +244,6 @@ extern far_ptr_t pxe_irq_chain; /* isr.c */ void pxe_init_isr(void); void pxe_cleanup_isr(void); -void pxe_poll(void); bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old); bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old); @@ -257,6 +256,7 @@ int pxe_getc(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(int); -- cgit v1.2.1 From e79652487a84453f967e03d168624ec476b36763 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 12 Apr 2011 04:54:53 -0700 Subject: pxe: Neuter pxe_idle_init The previous pxe_idle_init no longer makes sense as we are no longer using the udp core of the pxe stack. So gut pxe_idle_init. Signed-off-by: Eric W. Biederman --- core/fs/pxe/idle.c | 83 ------------------------------------------------------ 1 file changed, 83 deletions(-) (limited to 'core/fs') 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 #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) -- cgit v1.2.1 From 756d43cede941d410ba1f876cb7dd0fd0201fb90 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 21 Apr 2011 21:23:54 -0700 Subject: pxe: fix gcc 4.6 problems Remove unused variables, which give gcc 4.6 problems. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 3 +-- core/fs/pxe/tftp.c | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index cb2300e0..eacf4f87 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -137,13 +137,12 @@ void pxe_init_isr(void) void pxe_cleanup_isr(void) { static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; - int err; sched_hook_func = NULL; core_pm_hook = core_pm_null_hook; kill_thread(pxe_thread); memset(&undi_close, 0, sizeof(undi_close)); - err = pxe_call(PXENV_UNDI_CLOSE, &undi_close); + pxe_call(PXENV_UNDI_CLOSE, &undi_close); uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); } diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 4f21a487..06566d3f 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -227,7 +227,6 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, const uint8_t *timeout_ptr; jiffies_t timeout; jiffies_t oldtime; - uint16_t tid; uint16_t opcode; uint16_t blk_num; uint32_t opdata, *opdata_ptr; @@ -269,7 +268,6 @@ sendreq: oldtime = jiffies(); socket->tftp_remoteip = ip; - tid = socket->tftp_localport; /* TID(local port No) */ nbuf = netbuf_new(); netbuf_ref(nbuf, rrq_packet_buf, rrq_len); addr.addr = ip; -- cgit v1.2.1 From 8f5829b9f32fa9de1727255b1ef73fe4c0e6f2a7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 21 Apr 2011 21:24:33 -0700 Subject: pxe: isr: fix formatting Fix code formatting. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index eacf4f87..680c7ef6 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -85,26 +85,26 @@ static void pxe_process_irq(void) switch (isr.FuncFlag) { case PXENV_UNDI_ISR_OUT_DONE: - done = true; - break; + done = true; + break; case PXENV_UNDI_ISR_OUT_TRANSMIT: - /* Transmit complete - nothing for us to do */ - break; + /* Transmit complete - nothing for us to do */ + break; case PXENV_UNDI_ISR_OUT_RECEIVE: - undiif_input(&isr); - break; + undiif_input(&isr); + break; case PXENV_UNDI_ISR_OUT_BUSY: /* ISR busy, this should not happen */ - done = true; - break; + done = true; + break; default: /* Invalid return code, this should not happen */ - done = true; - break; + done = true; + break; } } } @@ -129,7 +129,8 @@ void pxe_init_isr(void) * avoid packet loss we need to move it into memory that we ourselves * manage, as soon as possible. */ - pxe_thread = start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); + pxe_thread = start_thread("pxe receive", 16384, -20, + pxe_receive_thread, NULL); core_pm_hook = __schedule; } -- cgit v1.2.1 From 2fa8eb93e5b2fc684d27f018c0a84341eaed5476 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 22 Apr 2011 15:57:33 -0700 Subject: lwip: handle UNDI stacks which need to be polled If the UNDI stack reports either IRQ 0 or does NOT report the NDIS IRQ supported flag, then poll the interrupt routine from the idle thread instead. This is somewhat limited; we really should have a chain of idle poll routines to support things like serial console. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 59 +++++++++++++++++++++++++++++++++++++++---------------- core/fs/pxe/pxe.h | 1 - 2 files changed, 42 insertions(+), 18 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 680c7ef6..ade3b0d2 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -18,32 +18,35 @@ bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) { far_ptr_t *entry; unsigned int vec; - + if (irq < 8) vec = irq + 0x08; else if (irq < 16) vec = (irq - 8) + 0x70; else return false; - + entry = (far_ptr_t *)(vec << 2); *old = *entry; entry->ptr = (uint32_t)isr; return true; } -bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) +static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) { far_ptr_t *entry; unsigned int vec; - + + if (!irq) + return true; /* Nothing to uninstall */ + if (irq < 8) vec = irq + 0x08; else if (irq < 16) vec = (irq - 8) + 0x70; else return false; - + entry = (far_ptr_t *)(vec << 2); if (entry->ptr != (uint32_t)isr) @@ -62,47 +65,57 @@ static void pxe_poll_wakeups(void) last_jiffies = now; __thread_process_timeouts(); } - + if (pxe_irq_pending) { pxe_irq_pending = 0; sem_up(&pxe_receive_thread_sem); } } +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_process_irq(void) { static __lowmem t_PXENV_UNDI_ISR isr; - uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */ + 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 */ + /* ISR busy, this should not happen */ done = true; break; - + default: - /* Invalid return code, this should not happen */ + /* Invalid return code, this should not happen */ done = true; break; } @@ -115,10 +128,16 @@ static void pxe_receive_thread(void *dummy) for (;;) { sem_down(&pxe_receive_thread_sem, 0); - pxe_process_irq(); + if (pxe_irq_vector || pxe_isr_poll()) + pxe_process_irq(); } } +static void pxe_do_isr_poll(void) +{ + sem_up(&pxe_receive_thread_sem); +} + void pxe_init_isr(void) { start_idle_thread(); @@ -132,6 +151,11 @@ void pxe_init_isr(void) pxe_thread = start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); core_pm_hook = __schedule; + + if (!pxe_irq_vector) { + /* No IRQ vector, need to poll */ + idle_hook = pxe_do_isr_poll; + } } @@ -139,10 +163,11 @@ void pxe_cleanup_isr(void) { static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; + idle_hook = NULL; 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); uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index d30e7839..0aa3c5a7 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -245,7 +245,6 @@ extern far_ptr_t pxe_irq_chain; void pxe_init_isr(void); void pxe_cleanup_isr(void); bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old); -bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old); /* pxe.c */ bool ip_ok(uint32_t); -- cgit v1.2.1 From 53ecbda7a9977735494c72748f54c164d5367e99 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 22 Apr 2011 19:35:36 -0700 Subject: pxe: put a semaphore around PXE calls Make sure we can't accidentally invoke the PXE stack from multiple threads (except for the ISR, which presumably has to allow for reentrancy, since the spec implies the ISR can be invoked from inside the PXE stack strategy routine.) Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index b9a7a04e..b4ff1f7b 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -6,6 +6,7 @@ #include #include #include "pxe.h" +#include "thread.h" #include #include #include @@ -222,11 +223,14 @@ const char *parse_dotquad(const char *ip_str, uint32_t *res) */ 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(®s, 0, sizeof regs); @@ -235,6 +239,8 @@ int pxe_call(int opcode, void *data) regs.edi.w[0] = OFFS(data); call16(pxenv, ®s, ®s); + sem_up(&pxe_sem); + return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */ } -- cgit v1.2.1 From da94a304bcb9645600901a692409680ac627e7d7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 23 Apr 2011 13:35:51 -0700 Subject: pxe: Add a much more general URL-parsing framework Add a URL-parsing framework that we can use for multiple protocols. Signed-off-by: H. Peter Anvin --- core/fs/pxe/url.h | 28 ++++++++ core/fs/pxe/urlparse.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 core/fs/pxe/url.h create mode 100644 core/fs/pxe/urlparse.c (limited to 'core/fs') diff --git a/core/fs/pxe/url.h b/core/fs/pxe/url.h new file mode 100644 index 00000000..baea2b70 --- /dev/null +++ b/core/fs/pxe/url.h @@ -0,0 +1,28 @@ +/* + * url.h + */ + +#ifndef CORE_PXE_URL_H +#define CORE_PXE_URL_H + +enum url_type { + URL_NORMAL, + URL_OLD_TFTP, + URL_PREFIX +}; + +struct url_info { + char *scheme; + char *user; + char *passwd; + char *host; + unsigned int port; + char *path; /* Includes query */ + enum url_type type; +}; + +void parse_url(struct url_info *ui, char *url); +char *url_escape_unsafe(const char *input); +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..77865b54 --- /dev/null +++ b/core/fs/pxe/urlparse.c @@ -0,0 +1,192 @@ +/* ----------------------------------------------------------------------- * + * + * 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 + * + * Decompose a URL into its components. + */ + +#include +#include +#include +#include "url.h" + +void parse_url(struct url_info *ui, char *url) +{ + char *p = url; + char *q, *r, *s; + + memset(ui, 0, sizeof *ui); + + q = strstr(p, "://"); + if (!q) { + q = strstr(p, "::"); + if (q) { + *q = '\0'; + ui->scheme = "tftp"; + ui->host = p; + ui->path = q+2; + ui->type = URL_OLD_TFTP; + return; + } else { + ui->path = p; + ui->type = URL_PREFIX; + return; + } + } + + 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 = atoi(r+1); + } +} + +char *url_escape_unsafe(const char *input) +{ + const char *p = input; + unsigned char c; + char *out, *q; + size_t n = 0; + + while ((c = *p++)) { + if (c < ' ' || c > '~') { + n += 3; /* Need escaping */ + } else { + n++; + } + } + + q = out = malloc(n+1); + while ((c = *p++)) { + if (c < ' ' || c > '~') { + q += snprintf(q, 3, "%%%02X", c); + } else { + *q++ = c; + } + } + + *q = '\0'; + + return out; +} + +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; +} + +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 -- cgit v1.2.1 From d6426caa6772e1887f07e35c15e2ba72a8a2bccd Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 23 Apr 2011 13:46:54 -0700 Subject: pxe: urlparse: add comments Add comments to the URL parser functions. Signed-off-by: H. Peter Anvin --- core/fs/pxe/urlparse.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c index 77865b54..459dca42 100644 --- a/core/fs/pxe/urlparse.c +++ b/core/fs/pxe/urlparse.c @@ -27,8 +27,6 @@ /* * urlparse.c - * - * Decompose a URL into its components. */ #include @@ -36,6 +34,11 @@ #include #include "url.h" +/* + * 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; @@ -97,6 +100,9 @@ void parse_url(struct url_info *ui, char *url) } } +/* + * Escapes unsafe characters in a URL. Returns a malloc'd buffer. + */ char *url_escape_unsafe(const char *input) { const char *p = input; @@ -136,6 +142,13 @@ static int hexdigit(char c) 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; -- cgit v1.2.1 From 0eefacc5fd49dae600ab86b14557578efd6a2508 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 23 Apr 2011 16:04:00 -0700 Subject: pxe: use the unified URL parsing framework Use the unified URL parsing framework for TFTP and HTTP. This should also make it easier to add new protocols (e.g. FTP) in the near future. Note that HTTP redirects are still handled wrong: they really should be sent all the way back to the top of URL parsing; there are sites in the field which redirect to FTP URLs, for example. Signed-off-by: H. Peter Anvin --- core/fs/pxe/dhcp_option.c | 9 +- core/fs/pxe/dnsresolv.c | 71 ++++++-------- core/fs/pxe/http.c | 98 +++++++------------ core/fs/pxe/pxe.c | 240 ++++++++-------------------------------------- core/fs/pxe/pxe.h | 39 +------- core/fs/pxe/tftp.c | 24 +++-- core/fs/pxe/tftp.h | 48 ++++++++++ core/fs/pxe/url.h | 13 ++- core/fs/pxe/urlparse.c | 146 +++++++++++++++------------- 9 files changed, 265 insertions(+), 423 deletions(-) create mode 100644 core/fs/pxe/tftp.h (limited to 'core/fs') diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c index 50f2de04..47031faf 100644 --- a/core/fs/pxe/dhcp_option.c +++ b/core/fs/pxe/dhcp_option.c @@ -48,13 +48,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 b827edbb..a4bbf1ff 100644 --- a/core/fs/pxe/dnsresolv.c +++ b/core/fs/pxe/dnsresolv.c @@ -50,42 +50,35 @@ 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. + * parse the ip_str and return the ip address with *res. + * return true if the whole string was consumed and the result + * was valid. + * */ -int dns_mangle(char **dst, const char *p) +static bool parse_dotquad(const char *ip_str, uint32_t *res) { - 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; + 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; - *count_ptr += 1; - *q++ = c; + ip = (ip << 8) | part; + part = 0; + p++; } + p--; - if (*count_ptr) - *q++ = 0; - - /* update the strings */ - *dst = q; - return dots; + *res = htonl(ip); + return *p == '\0'; } /* @@ -98,32 +91,22 @@ uint32_t dns_resolv(const char *name) { err_t err; struct ip_addr ip; - char dns_name[PKTBUF_SIZE]; - const char *src; - char *dst; + + /* If it is a valid dot quad, just return that value */ + if (parse_dotquad(name, &ip.addr)) + return ip.addr; /* Make sure we have at least one valid DNS server */ if (!dns_getserver(0).addr) return 0; - /* Copy the name to look up to ensure it is null terminated */ - for (dst = dns_name, src = name; *src; src++, dst++) { - int ch = *src; - if (ch == '\0' || ch == ':' || ch == '/') { - *dst = '\0'; - break; - } - *dst = ch; - } - - err = netconn_gethostbyname(dns_name, &ip); + err = netconn_gethostbyname(name, &ip); if (err) return 0; return ip.addr; } - /* * the one should be called from ASM file */ diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index 6af55d59..2f5a645a 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -1,7 +1,10 @@ #include +#include #include "pxe.h" #include "../../../version.h" -#include +#include "url.h" + +#define HTTP_PORT 80 static void http_close_file(struct inode *inode) { @@ -92,16 +95,14 @@ static bool append_ch(char *str, size_t size, size_t *pos, int ch) return success; } -void http_open(struct inode *inode, const char *url) +void http_open(struct url_info *url, struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); - char header_buf[512]; + char header_buf[4096]; int header_len; - const char *host, *path, *next; - size_t host_len; - uint16_t port; + const char *next; char field_name[20]; - char field_value[256]; + char field_value[1024]; size_t field_name_len, field_value_len; err_t err; enum state { @@ -116,7 +117,7 @@ void http_open(struct inode *inode, const char *url) st_eoh, } state; struct ip_addr addr; - char location[256]; + char location[1024], new_url[1024]; uint32_t content_length; /* same as inode->size */ size_t response_size; int status; @@ -130,49 +131,6 @@ void http_open(struct inode *inode, const char *url) restart: /* Reset all of the variables */ inode->size = content_length = -1; - location[0] = '\0'; - field_name[0] = '\0'; - field_value[0] = '\0'; - field_name_len = 0; - field_value_len = 0; - - /* Skip http:// */ - host = url + 7; - - /* Find the end of the hostname */ - next = host; - while (*next && *next != '/' && *next != ':') - next++; - host_len = next - host; - - /* Obvious url formatting errors */ - if (!*next || (!host_len && *next == ':')) - goto fail; - - /* Compute the dest port */ - port = 80; - if (*next == ':') { - port = 0; - for (next++; (*next >= '0' && *next <= '9'); next++) - port = (port * 10) * (*next - '0'); - } - - /* Ensure I have properly parsed the port */ - if (*next != '/') - goto fail; - - path = next; - - /* Resolve the hostname */ - if (!host_len) { - addr.addr = IPInfo.serverip; - } else { - if (parse_dotquad(host, &addr.addr) != (host + host_len)) { - addr.addr = dns_resolv(host); - if (!addr.addr) - goto fail; - } - } /* Start the http connection */ socket->conn = netconn_new(NETCONN_TCP); @@ -181,23 +139,31 @@ restart: return; } - err = netconn_connect(socket->conn, &addr, port); + addr.addr = url->ip; + if (!url->port) + url->port = HTTP_PORT; + err = netconn_connect(socket->conn, &addr, url->port); if (err) { printf("netconn_connect error %d\n", err); goto fail; } - header_len = snprintf(header_buf, sizeof header_buf, - "GET %s HTTP/1.0\r\n" - "Host: %*.*s\r\n" - "User-Agent: PXELINUX/%s\r\n" - "Connection: close\r\n" - "\r\n", - path, host_len, host_len, host, VERSION_STR); - - /* If we tried to overflow our buffer abort */ + strcpy(header_buf, "GET /"); + header_len = 5; + header_len += url_escape_unsafe(header_buf+5, url->path, + sizeof header_buf - 5); if (header_len > sizeof header_buf) - goto fail; + goto fail; /* Buffer overflow */ + header_len += snprintf(header_buf + header_len, + sizeof header_buf - header_len, + " HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: PXELINUX/%s\r\n" + "Connection: close\r\n" + "\r\n", + url->host, VERSION_STR); + if (header_len > sizeof header_buf) + goto fail; /* Buffer overflow */ err = netconn_write(socket->conn, header_buf, header_len, NETCONN_NOCOPY); if (err) { @@ -210,6 +176,8 @@ restart: pos = 0; status = 0; response_size = 0; + field_value_len = 0; + field_name_len = 0; while (state != st_eoh) { int ch = pxe_getc(inode); @@ -360,7 +328,11 @@ restart: redirect_count++; if (redirect_count > 5) goto fail; - url = location; + strlcpy(new_url, location, sizeof new_url); + parse_url(url, new_url); + url_set_ip(url); + http_close_file(inode); + /* XXX: This needs to go all the way back to scheme selection */ goto restart; break; default: diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index b4ff1f7b..58866afc 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -5,11 +5,13 @@ #include #include #include -#include "pxe.h" -#include "thread.h" #include #include #include +#include "pxe.h" +#include "thread.h" +#include "url.h" +#include "tftp.h" static uint16_t real_base_mem; /* Amount of DOS memory after freeing */ @@ -33,10 +35,11 @@ bool have_uuid = false; /* Common receive buffer */ __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); -static struct url_open { - const char *scheme; - void (*open)(struct inode *inode, const char *url); -} url_table[] = { +static struct url_scheme { + const char *name; + void (*open)(struct url_info *url, struct inode *inode); +} url_schemes[] = { + { "tftp", tftp_open }, { "http", http_open }, { NULL, NULL }, }; @@ -124,18 +127,6 @@ static void uchexbytes(char *dst, const void *src, int count) } } -/* - * 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 @@ -187,36 +178,6 @@ static int gendotquad(char *dst, uint32_t ip) return p - dst; } -/* - * parse the ip_str and return the ip address with *res. - * return the the string address after the ip string - * - */ -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; -} - /* * the ASM pxenv function wrapper, return 1 if error, or 0 * @@ -270,59 +231,13 @@ static int pxe_get_cached_info(int type) 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++; - } -} - /* * 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) { @@ -425,6 +340,17 @@ static uint32_t pxe_getfssec(struct file *file, char *buf, return bytes_read; } +/* + * Assign an IP address to a URL + */ +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 the specified connection @@ -452,119 +378,37 @@ static void __pxe_searchdir(const char *filename, struct file *file) { struct fs_info *fs = file->fs; struct inode *inode; - const char *np; - char *buf; - uint32_t ip = 0; - enum pxe_path_type path_type; char fullpath[2*FILENAME_MAX]; - uint16_t server_port = TFTP_PORT; /* TFTP server port */ + struct url_info url; + const struct url_scheme *us; inode = file->inode = NULL; - path_type = pxe_path_type(filename); - if (path_type == PXE_RELATIVE) { + strlcpy(fullpath, filename, sizeof fullpath); + parse_url(&url, fullpath); + if (url.type == URL_SUFFIX) { 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: - ip = IPInfo.serverip; /* Default server */ - break; - - case PXE_HOMESERVER: - filename = filename+2; - ip = IPInfo.serverip; - break; - - case PXE_TFTP: - np = strchr(filename, ':'); - if (parse_dotquad(filename, &ip) != np) - ip = dns_resolv(filename); - filename = np+2; - 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. - */ - filename = buf = fullpath; - while (*np && *np != ';') { - int v; - if (*np == '%' && (v = hexbyte(np+1)) > 0) { - *buf++ = v; - np += 3; - } else { - *buf++ = *np++; - } - } - *buf = '\0'; - break; + parse_url(&url, fullpath); } inode = allocate_socket(fs); if (!inode) return; /* Allocation failure */ - if (path_type == PXE_URL) { - struct url_open *entry; - np = strchr(filename, ':'); - - for (entry = url_table; entry->scheme; entry++) { - int scheme_len = strlen(entry->scheme); - if (scheme_len != (np - filename)) - continue; - if (memcmp(entry->scheme, filename, scheme_len) != 0) - continue; - entry->open(inode, filename); - goto done; - } - } + url_set_ip(&url); -#if GPXE - if (path_type == PXE_URL) { - if (has_gpxe) { - gpxe_open(inode, filename); - goto done; - } else { - static bool already = false; - if (!already) { - printf("URL syntax, but gPXE extensions not detected, " - "trying plain TFTP...\n"); - already = true; - } + for (us = url_schemes; us->name; us++) { + if (!strcmp(us->name, url.scheme)) { + us->open(&url, inode); + break; } } -#endif /* GPXE */ - if (!ip) - goto done; /* No server */ - - tftp_open(inode, ip, server_port, filename); -done: - if (!inode->size) { + if (inode->size) + file->inode = inode; + else free_socket(inode); - return; - } - file->inode = inode; + return; } @@ -612,10 +456,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); } /* @@ -624,9 +466,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); - - if (path_type == PXE_RELATIVE) + if (url_type(src) == URL_SUFFIX) strlcat(fs->cwd_name, src, sizeof fs->cwd_name); else strlcpy(fs->cwd_name, src, sizeof fs->cwd_name); diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 0aa3c5a7..bb833d60 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 @@ -26,7 +26,6 @@ /* * 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 /* */ @@ -45,43 +44,16 @@ 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 */ - struct pxenv_t { uint8_t signature[6]; /* PXENV+ */ uint16_t version; @@ -247,11 +219,12 @@ void pxe_cleanup_isr(void); bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old); /* pxe.c */ +struct url_info; bool ip_ok(uint32_t); int pxe_call(int, void *); extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); -const char *parse_dotquad(const char *ip_str, uint32_t *res); int pxe_getc(struct inode *inode); +void url_set_ip(struct url_info *); /* undiif.c */ int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw); @@ -261,7 +234,6 @@ void undiif_input(t_PXENV_UNDI_ISR *isr); void parse_dhcp(int); /* dnsresolv.c */ -int dns_mangle(char **, const char *); uint32_t dns_resolv(const char *); /* idle.c */ @@ -273,14 +245,13 @@ uint16_t get_port(void); void free_port(uint16_t port); /* tftp.c */ -void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, - const char *filename); +void tftp_open(struct url_info *url, struct inode *inode); /* gpxeurl.c */ void gpxe_open(struct inode *inode, const char *url); #define GPXE 1 /* http.c */ -void http_open(struct inode *inode, const char *url); +void http_open(struct url_info *url, struct inode *inode); #endif /* pxe.h */ diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 06566d3f..c5a757c8 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -1,6 +1,8 @@ #include +#include #include "pxe.h" -#include "lwip/api.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, @@ -207,8 +209,7 @@ static void tftp_get_packet(struct inode *inode) * @out: the lenght of this file, stores in file->file_len * */ -void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, - const char *filename) +void tftp_open(struct url_info *url, struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); char *buf; @@ -232,6 +233,16 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, uint32_t opdata, *opdata_ptr; struct ip_addr addr; + 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->fill_buffer = tftp_get_packet; socket->close = tftp_close_file; @@ -250,7 +261,7 @@ void tftp_open(struct inode *inode, uint32_t ip, uint16_t server_port, *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ buf += 2; - buf = stpcpy(buf, filename); + buf = stpcpy(buf, url->path); buf++; /* Point *past* the final NULL */ memcpy(buf, rrq_tail, sizeof rrq_tail); @@ -267,11 +278,10 @@ sendreq: return; /* No file available... */ oldtime = jiffies(); - socket->tftp_remoteip = ip; nbuf = netbuf_new(); netbuf_ref(nbuf, rrq_packet_buf, rrq_len); - addr.addr = ip; - netconn_sendto(socket->conn, nbuf, &addr, ntohs(server_port)); + addr.addr = socket->tftp_remoteip = url->ip; + netconn_sendto(socket->conn, nbuf, &addr, url->port); netbuf_delete(nbuf); /* If the WRITE call fails, we let the timeout take care of it... */ diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h new file mode 100644 index 00000000..b8b7f10e --- /dev/null +++ b/core/fs/pxe/tftp.h @@ -0,0 +1,48 @@ +/* ----------------------------------------------------------------------- + * + * 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 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 index baea2b70..53984f3a 100644 --- a/core/fs/pxe/url.h +++ b/core/fs/pxe/url.h @@ -5,10 +5,13 @@ #ifndef CORE_PXE_URL_H #define CORE_PXE_URL_H +#include +#include + enum url_type { - URL_NORMAL, - URL_OLD_TFTP, - URL_PREFIX + 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 { @@ -16,13 +19,15 @@ struct url_info { 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); -char *url_escape_unsafe(const char *input); +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 index 459dca42..bc55197b 100644 --- a/core/fs/pxe/urlparse.c +++ b/core/fs/pxe/urlparse.c @@ -34,6 +34,26 @@ #include #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 @@ -43,93 +63,91 @@ void parse_url(struct url_info *ui, char *url) { char *p = url; char *q, *r, *s; + int c; memset(ui, 0, sizeof *ui); - q = strstr(p, "://"); - if (!q) { - q = strstr(p, "::"); + 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->scheme = "tftp"; - ui->host = p; - ui->path = q+2; - ui->type = URL_OLD_TFTP; - return; + ui->path = q+1; + q = strchr(q+1, '#'); + if (q) + *q = '\0'; } else { - ui->path = p; - ui->type = URL_PREFIX; - return; + ui->path = ""; } - } - - ui->type = URL_NORMAL; - - ui->scheme = p; - *q = '\0'; - p = q+3; - - q = strchr(p, '/'); - if (q) { + + 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->path = q+1; - q = strchr(q+1, '#'); - if (q) - *q = '\0'; + ui->scheme = "tftp"; + ui->host = p; + ui->path = q+2; + ui->type = URL_OLD_TFTP; } 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 = atoi(r+1); + ui->path = p; + ui->type = URL_SUFFIX; } } /* - * Escapes unsafe characters in a URL. Returns a malloc'd buffer. + * Escapes unsafe characters in a URL. + * This does *not* escape things like query characters! + * Returns the number of characters in the total output. */ -char *url_escape_unsafe(const char *input) +size_t url_escape_unsafe(char *output, const char *input, size_t bufsize) { - const char *p = input; + static const char uchexchar[] = "0123456789ABCDEF"; + const char *p; unsigned char c; - char *out, *q; + char *q; size_t n = 0; - while ((c = *p++)) { - if (c < ' ' || c > '~') { - n += 3; /* Need escaping */ - } else { - n++; - } - } - - q = out = malloc(n+1); - while ((c = *p++)) { - if (c < ' ' || c > '~') { - q += snprintf(q, 3, "%%%02X", c); + 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 { - *q++ = c; + if (++n < bufsize) *q++ = c; } } *q = '\0'; - - return out; + return n; } static int hexdigit(char c) -- cgit v1.2.1 From 6c5e4a4bcfc991a6e578f65976891bcde31d0c56 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 23 Apr 2011 20:48:42 -0700 Subject: pxe: move operations common to TCP-based protocols to a common file Move operations that are common to all TCP-based protocols into a common file, tcp.c. Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 66 ++++++------------------------------------------- core/fs/pxe/pxe.h | 8 ++++-- core/fs/pxe/tcp.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 60 deletions(-) create mode 100644 core/fs/pxe/tcp.c (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index 2f5a645a..8d197557 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -6,56 +6,6 @@ #define HTTP_PORT 80 -static void http_close_file(struct inode *inode) -{ - struct pxe_pvt_inode *socket = PVT(inode); - if (socket->buf) { - netbuf_delete(socket->buf); - socket->buf = NULL; - } - if (socket->conn) { - netconn_delete(socket->conn); - socket->conn = NULL; - } -} - -static void http_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->buf) { - if (netbuf_next(socket->buf) < 0) { - netbuf_delete(socket->buf); - socket->buf = NULL; - } - } - /* If needed get a new netbuf */ - if (!socket->buf) { - socket->buf = netconn_recv(socket->conn); - if (!socket->buf) { - socket->tftp_goteof = 1; - if (inode->size == -1) - inode->size = socket->tftp_filepos; - http_close_file(inode); - return; - } - } - /* Report the current fragment of the netbuf */ - err = netbuf_data(socket->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; -} - static bool is_tspecial(int ch) { bool tspecial = false; @@ -65,7 +15,7 @@ static bool is_tspecial(int ch) case '/': case '[': case ']': case '?': case '=': case '{': case '}': case ' ': case '\t': tspecial = true; - break; + break; } return tspecial; } @@ -86,7 +36,7 @@ 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; + success = false; } else { str[*pos] = ch; str[*pos + 1] = '\0'; @@ -124,8 +74,8 @@ void http_open(struct url_info *url, struct inode *inode) int pos; int redirect_count; - socket->fill_buffer = http_fill_buffer; - socket->close = http_close_file; + socket->fill_buffer = tcp_fill_buffer; + socket->close = tcp_close_file; redirect_count = 0; restart: @@ -268,7 +218,7 @@ restart: /* Bogus cases try to recover */ else if (ch == '\n') state = st_fieldfirst; - else + else state = st_skipline; break; @@ -300,7 +250,7 @@ restart: if (ch == '\n') state = st_fieldfirst; break; - + case st_eoh: break; /* Should never happen */ } @@ -331,7 +281,7 @@ restart: strlcpy(new_url, location, sizeof new_url); parse_url(url, new_url); url_set_ip(url); - http_close_file(inode); + tcp_close_file(inode); /* XXX: This needs to go all the way back to scheme selection */ goto restart; break; @@ -342,6 +292,6 @@ restart: return; fail: inode->size = 0; - http_close_file(inode); + tcp_close_file(inode); return; } diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index bb833d60..7d5c43ed 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -52,7 +52,7 @@ static inline int hexval(char c) #define DNS_MAX_SERVERS 4 /* Max no of DNS servers */ /* - * structures + * structures */ struct pxenv_t { uint8_t signature[6]; /* PXENV+ */ @@ -205,7 +205,7 @@ static inline uint32_t gateway(uint32_t ip) } /* - * functions + * functions */ /* pxeisr.inc */ @@ -254,4 +254,8 @@ void gpxe_open(struct inode *inode, const char *url); /* http.c */ void http_open(struct url_info *url, struct inode *inode); +/* tcp.c */ +void tcp_close_file(struct inode *inode); +void tcp_fill_buffer(struct inode *inode); + #endif /* pxe.h */ diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c new file mode 100644 index 00000000..af0cffcb --- /dev/null +++ b/core/fs/pxe/tcp.c @@ -0,0 +1,72 @@ +/* ----------------------------------------------------------------------- * + * + * 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 +#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->buf) { + netbuf_delete(socket->buf); + socket->buf = NULL; + } + if (socket->conn) { + netconn_delete(socket->conn); + socket->conn = 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->buf) { + if (netbuf_next(socket->buf) < 0) { + netbuf_delete(socket->buf); + socket->buf = NULL; + } + } + /* If needed get a new netbuf */ + if (!socket->buf) { + socket->buf = netconn_recv(socket->conn); + if (!socket->buf) { + socket->tftp_goteof = 1; + if (inode->size == -1) + inode->size = socket->tftp_filepos; + tcp_close_file(inode); + return; + } + } + /* Report the current fragment of the netbuf */ + err = netbuf_data(socket->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; +} -- cgit v1.2.1 From d05e01cf06aac976a2ba274ec7a0fbd9830782a6 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 23 Apr 2011 21:04:58 -0700 Subject: pxe: make tftp_pktbuf a dynamic buffer We don't need tftp_pktbuf for the TCP-based protocols, so allocate it on demand. It should be possible to get rid of it for TFTP as well. Signed-off-by: H. Peter Anvin --- core/fs/pxe/gpxeurl.c | 7 ++++++- core/fs/pxe/pxe.h | 4 +--- core/fs/pxe/tftp.c | 29 ++++++++++++++++++++--------- core/fs/pxe/tftp.h | 6 ++++++ 4 files changed, 33 insertions(+), 13 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/gpxeurl.c b/core/fs/pxe/gpxeurl.c index 16152818..6f456bbb 100644 --- a/core/fs/pxe/gpxeurl.c +++ b/core/fs/pxe/gpxeurl.c @@ -8,6 +8,8 @@ static void gpxe_close_file(struct inode *inode) file_close.FileHandle = socket->tftp_remoteport; pxe_call(PXENV_FILE_CLOSE, &file_close); + + free(socket->tftp_pktbuf); } /** @@ -68,6 +70,10 @@ void gpxe_open(struct inode *inode, const char *url) 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); @@ -82,4 +88,3 @@ void gpxe_open(struct inode *inode, const char *url) } #endif /* GPXE */ - diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 7d5c43ed..db1a566f 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -26,8 +26,6 @@ /* * Some basic defines... */ -#define TFTP_BLOCKSIZE_LG2 9 -#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2) #define PKTBUF_SIZE 2048 /* */ #define is_digit(c) (((c) >= '0') && ((c) <= '9')) @@ -141,7 +139,7 @@ struct pxe_pvt_inode { uint8_t tftp_unused[3]; /* Currently unused */ void (*fill_buffer)(struct inode *inode); void (*close)(struct inode *inode); - char tftp_pktbuf[PKTBUF_SIZE]; + char *tftp_pktbuf; /* Packet buffer */ } __attribute__ ((packed)); #define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt)) diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index c5a757c8..89b21aeb 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -37,6 +37,7 @@ static void tftp_close_file(struct inode *inode) netconn_delete(socket->conn); socket->conn = NULL; } + free(socket->tftp_pktbuf); } /** @@ -313,7 +314,6 @@ wait_pkt: /* filesize <- -1 == unknown */ inode->size = -1; - /* Default blksize unless blksize option negotiated */ socket->tftp_blksize = TFTP_BLOCKSIZE; buffersize = nbuf_len - 2; /* bytes after opcode */ if (buffersize < 0) @@ -349,8 +349,13 @@ wait_pkt: goto wait_pkt; socket->tftp_lastpkt = blk_num; if (buffersize > TFTP_BLOCKSIZE) - goto err_reply; /* Corrupt */ - else if (buffersize < TFTP_BLOCKSIZE) { + goto err_reply; /* Corrupt */ + + socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE); + 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 @@ -435,23 +440,29 @@ wait_pkt: } *opdata_ptr = opdata; } + + /* Parsing successful, allocate buffer */ + socket->tftp_pktbuf = malloc(socket->tftp_blksize); + if (!socket->tftp_pktbuf) + goto err_reply; break; 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) { + free(socket->tftp_pktbuf); netconn_delete(socket->conn); socket->conn = NULL; } 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(); } - diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h index b8b7f10e..114c221f 100644 --- a/core/fs/pxe/tftp.h +++ b/core/fs/pxe/tftp.h @@ -22,6 +22,12 @@ */ #define TFTP_PORT 69 +/* + * TFTP default block size + */ +#define TFTP_BLOCKSIZE_LG2 9 +#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2) + /* * TFTP operation codes */ -- cgit v1.2.1 From e23631e677369defe61a300c728f0bb6a345b7db Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 12:44:31 -0700 Subject: pxe: tcp: always call the close method Always call the designated close method, instead of assuming it is tcp_close_file(). This isn't the case for FTP, which also needs to manage the control connection. Signed-off-by: H. Peter Anvin --- core/fs/pxe/tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c index af0cffcb..109f803d 100644 --- a/core/fs/pxe/tcp.c +++ b/core/fs/pxe/tcp.c @@ -24,6 +24,7 @@ void tcp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); + if (socket->buf) { netbuf_delete(socket->buf); socket->buf = NULL; @@ -55,7 +56,7 @@ void tcp_fill_buffer(struct inode *inode) socket->tftp_goteof = 1; if (inode->size == -1) inode->size = socket->tftp_filepos; - tcp_close_file(inode); + socket->close(inode); return; } } -- cgit v1.2.1 From 2c7e107a97759d6c7527a85ef8bb6d5bcb85a948 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 13:46:45 -0700 Subject: urlparse: fix url_unescape() Fix double pointer advacing in url_unescape(). Signed-off-by: H. Peter Anvin --- core/fs/pxe/urlparse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c index bc55197b..6b73ddb6 100644 --- a/core/fs/pxe/urlparse.c +++ b/core/fs/pxe/urlparse.c @@ -174,7 +174,7 @@ char *url_unescape(char *buffer, char terminator) unsigned char c; int x, y; - while ((c = *p++)) { + while ((c = *p)) { if (c == terminator) { *q = '\0'; return p; -- cgit v1.2.1 From 303bd1a3e1c8642934d40914880a42ae98610599 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 13:48:17 -0700 Subject: pxe: tcp: close the connection before freeing the buffers As long as the connection is opened, we may end up receiving data, so don't free the receive buffers until after the connection is closed. Signed-off-by: H. Peter Anvin --- core/fs/pxe/tcp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c index 109f803d..759ec9f3 100644 --- a/core/fs/pxe/tcp.c +++ b/core/fs/pxe/tcp.c @@ -25,14 +25,14 @@ void tcp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); - if (socket->buf) { - netbuf_delete(socket->buf); - socket->buf = NULL; - } if (socket->conn) { netconn_delete(socket->conn); socket->conn = NULL; } + if (socket->buf) { + netbuf_delete(socket->buf); + socket->buf = NULL; + } } void tcp_fill_buffer(struct inode *inode) -- cgit v1.2.1 From 5fbdb5eba5640f1147474fafa73fbb66c9a406a9 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 13:49:30 -0700 Subject: pxe: add support for FTP Add support for the FTP protocol. Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ core/fs/pxe/pxe.c | 3 +- core/fs/pxe/pxe.h | 9 +- 3 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 core/fs/pxe/ftp.c (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c new file mode 100644 index 00000000..0c033553 --- /dev/null +++ b/core/fs/pxe/ftp.c @@ -0,0 +1,267 @@ +/* + * ftp.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pxe.h" +#include "thread.h" +#include "strbuf.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->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) { + //printf("%c", c); + + if (c == '\n') { + pos = 0; + if (done) { + if (pn) { + pn += ps; + if (pn_ptr) + *pn_ptr = pn; + } + //printf("FTP response: %u\n", code); + return code; + } + 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; + + //printf("In ftp_close_file\n"); + + ctlsock = socket->ctl ? PVT(socket->ctl) : NULL; + if (ctlsock->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); +} + +void ftp_open(struct url_info *url, struct inode *inode) +{ + struct pxe_pvt_inode *socket = PVT(inode); + struct pxe_pvt_inode *ctlsock; + struct ip_addr addr; + uint8_t pasv_data[6]; + int pasv_bytes; + int resp; + err_t err; + + inode->size = 0; + + if (!url->port) + url->port = 21; + + url_unescape(url->path, 0); + + socket->fill_buffer = tcp_fill_buffer; + socket->close = ftp_close_file; + + /* Allocate a socket for the control connection */ + socket->ctl = alloc_inode(inode->fs, 0, sizeof(struct pxe_pvt_inode)); + if (!socket->ctl) + return; + ctlsock = PVT(socket->ctl); + ctlsock->fill_buffer = tcp_fill_buffer; + ctlsock->close = tcp_close_file; + + ctlsock->conn = netconn_new(NETCONN_TCP); + if (!ctlsock->conn) + goto err_free; + addr.addr = url->ip; + err = netconn_connect(ctlsock->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 = "pxelinux@"; + + 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; + } + + 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); + //printf("%u PASV %u bytes %u,%u,%u,%u,%u,%u\n", + //resp, pasv_bytes, pasv_data[0], pasv_data[1], pasv_data[2], + //pasv_data[3], pasv_data[4], pasv_data[5]); + if (resp != 227 || pasv_bytes != 6) + goto err_disconnect; + + socket->conn = netconn_new(NETCONN_TCP); + if (!socket->conn) + goto err_disconnect; + err = netconn_connect(socket->conn, (struct ip_addr *)&pasv_data[0], + ntohs(*(uint16_t *)&pasv_data[4])); + if (err) + goto err_disconnect; + + resp = ftp_cmd_response(socket->ctl, "RETR", url->path, NULL, NULL); + if (resp != 125 && resp != 150) + goto err_disconnect; + + inode->size = -1; + return; /* Sucess! */ + +err_disconnect: + if (ctlsock->conn) + netconn_write(ctlsock->conn, "QUIT\r\n", 6, NETCONN_NOCOPY); + if (socket->conn) + netconn_delete(socket->conn); + if (ctlsock->buf) + netbuf_delete(ctlsock->buf); +err_delete: + if (ctlsock->conn) + netconn_delete(ctlsock->conn); +err_free: + free_socket(socket->ctl); +} diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 58866afc..71532d63 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -41,6 +41,7 @@ static struct url_scheme { } url_schemes[] = { { "tftp", tftp_open }, { "http", http_open }, + { "ftp", ftp_open }, { NULL, NULL }, }; @@ -63,7 +64,7 @@ static struct inode *allocate_socket(struct fs_info *fs) return inode; } -static void free_socket(struct inode *inode) +void free_socket(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index db1a566f..699cf658 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -137,10 +137,11 @@ struct pxe_pvt_inode { 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) */ void (*fill_buffer)(struct inode *inode); void (*close)(struct inode *inode); - char *tftp_pktbuf; /* Packet buffer */ -} __attribute__ ((packed)); +}; #define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt)) @@ -223,6 +224,7 @@ int pxe_call(int, void *); extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); int pxe_getc(struct inode *inode); void url_set_ip(struct url_info *); +void free_socket(struct inode *inode); /* undiif.c */ int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw); @@ -252,6 +254,9 @@ void gpxe_open(struct inode *inode, const char *url); /* http.c */ void http_open(struct url_info *url, struct inode *inode); +/* ftp.c */ +void ftp_open(struct url_info *url, struct inode *inode); + /* tcp.c */ void tcp_close_file(struct inode *inode); void tcp_fill_buffer(struct inode *inode); -- cgit v1.2.1 From 17e4fda81778ca19aae85a630c302230b3802b43 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 13:59:18 -0700 Subject: pxe: move redirect processing to the URL-parsing level Move redirect processing out of the HTTP code and into the top URL-processing level. This both makes the code simpler and also deals with absurdities like HTTP redirecting to an FTP URL (which is legal.) Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 3 ++- core/fs/pxe/http.c | 21 +++++---------------- core/fs/pxe/pxe.c | 41 +++++++++++++++++++++++++---------------- core/fs/pxe/pxe.h | 6 +++--- core/fs/pxe/tftp.c | 4 +++- 5 files changed, 38 insertions(+), 37 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 0c033553..ca4b3d6d 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -169,7 +169,7 @@ static void ftp_close_file(struct inode *inode) ftp_free(inode); } -void ftp_open(struct url_info *url, struct inode *inode) +void ftp_open(struct url_info *url, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); struct pxe_pvt_inode *ctlsock; @@ -179,6 +179,7 @@ void ftp_open(struct url_info *url, struct inode *inode) int resp; err_t err; + (void)redir; /* FTP does not redirect */ inode->size = 0; if (!url->port) diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index 8d197557..f46cd4cf 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -45,7 +45,7 @@ static bool append_ch(char *str, size_t size, size_t *pos, int ch) return success; } -void http_open(struct url_info *url, struct inode *inode) +void http_open(struct url_info *url, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); char header_buf[4096]; @@ -67,18 +67,15 @@ void http_open(struct url_info *url, struct inode *inode) st_eoh, } state; struct ip_addr addr; - char location[1024], new_url[1024]; + static char location[FILENAME_MAX]; uint32_t content_length; /* same as inode->size */ size_t response_size; int status; int pos; - int redirect_count; socket->fill_buffer = tcp_fill_buffer; socket->close = tcp_close_file; - redirect_count = 0; -restart: /* Reset all of the variables */ inode->size = content_length = -1; @@ -193,7 +190,7 @@ restart: /* Skip leading whitespace */ while (isspace(*next)) next++; - strcpy(location, next); + strlcpy(location, next, sizeof location); } /* Start the field name and field value afress */ field_name_len = 1; @@ -275,16 +272,8 @@ restart: /* A redirect */ if (!location[0]) goto fail; - redirect_count++; - if (redirect_count > 5) - goto fail; - strlcpy(new_url, location, sizeof new_url); - parse_url(url, new_url); - url_set_ip(url); - tcp_close_file(inode); - /* XXX: This needs to go all the way back to scheme selection */ - goto restart; - break; + *redir = location; + goto fail; default: goto fail; break; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 71532d63..0f768046 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -37,7 +37,7 @@ __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); static struct url_scheme { const char *name; - void (*open)(struct url_info *url, struct inode *inode); + void (*open)(struct url_info *url, struct inode *inode, const char **redir); } url_schemes[] = { { "tftp", tftp_open }, { "http", http_open }, @@ -382,27 +382,36 @@ static void __pxe_searchdir(const char *filename, struct file *file) char fullpath[2*FILENAME_MAX]; struct url_info url; const struct url_scheme *us; + int redirect_count = 0; inode = file->inode = NULL; - strlcpy(fullpath, filename, sizeof fullpath); - parse_url(&url, fullpath); - if (url.type == URL_SUFFIX) { - snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename); - parse_url(&url, fullpath); - } - - inode = allocate_socket(fs); - if (!inode) - return; /* Allocation failure */ + while (filename) { + if (redirect_count++ > 5) + break; - url_set_ip(&url); + strlcpy(fullpath, filename, sizeof fullpath); + parse_url(&url, fullpath); + if (url.type == URL_SUFFIX) { + snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename); + parse_url(&url, fullpath); + } - for (us = url_schemes; us->name; us++) { - if (!strcmp(us->name, url.scheme)) { - us->open(&url, inode); - break; + inode = allocate_socket(fs); + if (!inode) + return; /* Allocation failure */ + + url_set_ip(&url); + + filename = NULL; + for (us = url_schemes; us->name; us++) { + if (!strcmp(us->name, url.scheme)) { + us->open(&url, inode, &filename); + break; + } } + + /* filename here is set on a redirect */ } if (inode->size) diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 699cf658..b23f7a72 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -245,17 +245,17 @@ uint16_t get_port(void); void free_port(uint16_t port); /* tftp.c */ -void tftp_open(struct url_info *url, struct inode *inode); +void tftp_open(struct url_info *url, struct inode *inode, const char **redir); /* gpxeurl.c */ void gpxe_open(struct inode *inode, const char *url); #define GPXE 1 /* http.c */ -void http_open(struct url_info *url, struct inode *inode); +void http_open(struct url_info *url, struct inode *inode, const char **redir); /* ftp.c */ -void ftp_open(struct url_info *url, struct inode *inode); +void ftp_open(struct url_info *url, struct inode *inode, const char **redir); /* tcp.c */ void tcp_close_file(struct inode *inode); diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 89b21aeb..637f0e90 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -210,7 +210,7 @@ static void tftp_get_packet(struct inode *inode) * @out: the lenght of this file, stores in file->file_len * */ -void tftp_open(struct url_info *url, struct inode *inode) +void tftp_open(struct url_info *url, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); char *buf; @@ -234,6 +234,8 @@ void tftp_open(struct url_info *url, struct inode *inode) uint32_t opdata, *opdata_ptr; struct ip_addr addr; + (void)redir; /* TFTP does not redirect */ + if (url->type != URL_OLD_TFTP) { /* * The TFTP URL specification allows the TFTP to end with a -- cgit v1.2.1 From 782a60a49cf263e9ce99897cef530deb02af00dd Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 14:02:39 -0700 Subject: pxe: make url_set_ip() static url_set_ip() is now only used in one place, so make it static. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 2 +- core/fs/pxe/pxe.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 0f768046..5789bb63 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -344,7 +344,7 @@ static uint32_t pxe_getfssec(struct file *file, char *buf, /* * Assign an IP address to a URL */ -void url_set_ip(struct url_info *url) +static void url_set_ip(struct url_info *url) { url->ip = 0; if (url->host) diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index b23f7a72..841e5ef4 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -223,7 +223,6 @@ bool ip_ok(uint32_t); int pxe_call(int, void *); extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); int pxe_getc(struct inode *inode); -void url_set_ip(struct url_info *); void free_socket(struct inode *inode); /* undiif.c */ -- cgit v1.2.1 From 9c136a34a30708efa50545cb8859e937964bed2f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 14:42:56 -0700 Subject: pxe: continue to bounce URLs with unknown schemes to gPXE If we're running on top of a gPXE/iPXE stack, and get a URL we don't know, continue to bounce it to gPXE. It isn't entirely clear how well this will actually work with an UNDI-based network stack, however... Signed-off-by: H. Peter Anvin --- core/fs/pxe/gpxeurl.c | 2 -- core/fs/pxe/pxe.c | 22 +++++++++++++++++++++- core/fs/pxe/tftp.c | 3 --- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/gpxeurl.c b/core/fs/pxe/gpxeurl.c index 6f456bbb..6bbae3c2 100644 --- a/core/fs/pxe/gpxeurl.c +++ b/core/fs/pxe/gpxeurl.c @@ -8,8 +8,6 @@ static void gpxe_close_file(struct inode *inode) file_close.FileHandle = socket->tftp_remoteport; pxe_call(PXENV_FILE_CLOSE, &file_close); - - free(socket->tftp_pktbuf); } /** diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 5789bb63..939ce719 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -68,6 +68,7 @@ void free_socket(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); + free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */ free_port(socket->tftp_localport); free_inode(inode); } @@ -380,9 +381,13 @@ static void __pxe_searchdir(const char *filename, struct file *file) struct fs_info *fs = file->fs; struct inode *inode; char fullpath[2*FILENAME_MAX]; +#ifdef GPXE + char urlsave[2*FILENAME_MAX]; +#endif struct url_info url; - const struct url_scheme *us; + const struct url_scheme *us = NULL; int redirect_count = 0; + bool found_scheme = false; inode = file->inode = NULL; @@ -391,9 +396,15 @@ static void __pxe_searchdir(const char *filename, struct file *file) break; strlcpy(fullpath, filename, sizeof fullpath); +#ifdef GPXE + strcpy(urlsave, fullpath); +#endif parse_url(&url, fullpath); if (url.type == URL_SUFFIX) { snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename); +#ifdef GPXE + strcpy(urlsave, fullpath); +#endif parse_url(&url, fullpath); } @@ -404,9 +415,11 @@ static void __pxe_searchdir(const char *filename, struct file *file) url_set_ip(&url); filename = NULL; + found_scheme = false; for (us = url_schemes; us->name; us++) { if (!strcmp(us->name, url.scheme)) { us->open(&url, inode, &filename); + found_scheme = true; break; } } @@ -414,6 +427,13 @@ static void __pxe_searchdir(const char *filename, struct file *file) /* filename here is set on a redirect */ } +#ifdef GPXE + if (!found_scheme) { + /* No URL scheme found, hand it to GPXE */ + gpxe_open(inode, urlsave); + } +#endif + if (inode->size) file->inode = inode; else diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 637f0e90..77efe49a 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -37,7 +37,6 @@ static void tftp_close_file(struct inode *inode) netconn_delete(socket->conn); socket->conn = NULL; } - free(socket->tftp_pktbuf); } /** @@ -461,10 +460,8 @@ err_reply: done: if (!inode->size) { - free(socket->tftp_pktbuf); netconn_delete(socket->conn); socket->conn = NULL; } return; - } -- cgit v1.2.1 From f33ddd93014612d38f4ec8bf847056b696d3b5cc Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 14:51:31 -0700 Subject: pxe: allow an empty input to dns_resolv() If dns_resolv() gets an empty input, just return failure. Signed-off-by: H. Peter Anvin --- core/fs/pxe/dnsresolv.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'core/fs') diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c index a4bbf1ff..350dfe31 100644 --- a/core/fs/pxe/dnsresolv.c +++ b/core/fs/pxe/dnsresolv.c @@ -92,6 +92,14 @@ uint32_t dns_resolv(const char *name) err_t err; struct ip_addr ip; + /* + * 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; + /* If it is a valid dot quad, just return that value */ if (parse_dotquad(name, &ip.addr)) return ip.addr; -- cgit v1.2.1 From 2061baaf73bbb5f83e675552d6097693da200eef Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 15:15:07 -0700 Subject: pxe, ftp: remove a bogus include Remove an include of a header file which never actually existed. Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 1 - 1 file changed, 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index ca4b3d6d..4efcedb6 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -13,7 +13,6 @@ #include #include "pxe.h" #include "thread.h" -#include "strbuf.h" #include "url.h" static int ftp_cmd_response(struct inode *inode, const char *cmd, -- cgit v1.2.1 From 0502b06c879cace4c5a27698f8ec8d8bad8e075a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 15:16:23 -0700 Subject: pxe, tftp: let lwIP manage port numbers lwIP needs to manage port numbers for TCP and for DNS, so just let it do it for TFTP as well. Signed-off-by: H. Peter Anvin --- core/fs/pxe/portnum.c | 68 --------------------------------------------------- core/fs/pxe/pxe.c | 3 --- core/fs/pxe/pxe.h | 9 ++----- core/fs/pxe/tftp.c | 9 ++++--- 4 files changed, 7 insertions(+), 82 deletions(-) delete mode 100644 core/fs/pxe/portnum.c (limited to 'core/fs') 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 -#include -#include -#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 939ce719..2bcb27f5 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -56,8 +56,6 @@ 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 */ } @@ -69,7 +67,6 @@ void free_socket(struct inode *inode) struct pxe_pvt_inode *socket = PVT(inode); free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */ - free_port(socket->tftp_localport); free_inode(inode); } diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 841e5ef4..9dec35e2 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -127,10 +127,9 @@ struct netbuf; struct pxe_pvt_inode { struct netconn *conn; /* lwip network connection */ struct netbuf *buf; /* lwip cached buffer */ - 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_remoteip; /* Remote IP address (0 = disconnected) */ + 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 (NBO) */ @@ -239,10 +238,6 @@ uint32_t dns_resolv(const char *); 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, struct inode *inode, const char **redir); diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 77efe49a..e7a6e01a 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -30,7 +30,7 @@ static void tftp_error(struct inode *file, uint16_t errnum, static void tftp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); - if (socket->tftp_localport != 0) { + if (socket->tftp_remoteip) { tftp_error(inode, 0, "No error, file close"); } if (socket->conn) { @@ -242,22 +242,23 @@ void tftp_open(struct url_info *url, struct inode *inode, const char **redir) */ url_unescape(url->path, ';'); } + if (!url->port) url->port = TFTP_PORT; socket->fill_buffer = tftp_get_packet; socket->close = tftp_close_file; - socket->conn = netconn_new(NETCONN_UDP); if (!socket->conn) return; socket->conn->recv_timeout = 15; /* A 15 ms recv timeout... */ - err = netconn_bind(socket->conn, NULL, ntohs(socket->tftp_localport)); + err = netconn_bind(socket->conn, NULL, 0); if (err) { printf("netconn_bind error %d\n", err); return; } + socket->tftp_remoteip = url->ip; buf = rrq_packet_buf; *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ @@ -282,7 +283,7 @@ sendreq: nbuf = netbuf_new(); netbuf_ref(nbuf, rrq_packet_buf, rrq_len); - addr.addr = socket->tftp_remoteip = url->ip; + addr.addr = socket->tftp_remoteip; netconn_sendto(socket->conn, nbuf, &addr, url->port); netbuf_delete(nbuf); -- cgit v1.2.1 From 5d963127a55c1ad44af5e530aacd3933ffd0b4ca Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 15:40:01 -0700 Subject: pxe, tftp: unbreak the TFTP state machine Some of the code motion had broken the TFTP state machine, make it work properly again. Furthermore, remove some no longer necessary fields in the pxe inode structure. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.h | 1 - core/fs/pxe/tftp.c | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 9dec35e2..a685d8eb 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -128,7 +128,6 @@ struct pxe_pvt_inode { struct netconn *conn; /* lwip network connection */ struct netbuf *buf; /* lwip cached buffer */ uint16_t tftp_remoteport; /* Remote port number */ - uint32_t tftp_remoteip; /* Remote IP address (0 = disconnected) */ uint32_t tftp_filepos; /* bytes downloaded (including buffer) */ uint32_t tftp_blksize; /* Block size for this connection(*) */ uint16_t tftp_bytesleft; /* Unclaimed data bytes */ diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index e7a6e01a..5aafd8eb 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -30,7 +30,7 @@ static void tftp_error(struct inode *file, uint16_t errnum, static void tftp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); - if (socket->tftp_remoteip) { + if (!socket->tftp_goteof) { tftp_error(inode, 0, "No error, file close"); } if (socket->conn) { @@ -258,7 +258,6 @@ void tftp_open(struct url_info *url, struct inode *inode, const char **redir) printf("netconn_bind error %d\n", err); return; } - socket->tftp_remoteip = url->ip; buf = rrq_packet_buf; *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ @@ -283,7 +282,7 @@ sendreq: nbuf = netbuf_new(); netbuf_ref(nbuf, rrq_packet_buf, rrq_len); - addr.addr = socket->tftp_remoteip; + addr.addr = url->ip; netconn_sendto(socket->conn, nbuf, &addr, url->port); netbuf_delete(nbuf); @@ -299,7 +298,7 @@ wait_pkt: } else { /* Make sure the packet actually came from the server */ bool ok_source; - ok_source = netbuf_fromaddr(nbuf)->addr == socket->tftp_remoteip; + ok_source = netbuf_fromaddr(nbuf)->addr == url->ip; nbuf_len = netbuf_len(nbuf); if (nbuf_len <= PKTBUF_SIZE) netbuf_copy(nbuf, packet_buf, nbuf_len); @@ -311,7 +310,6 @@ wait_pkt: } } - socket->tftp_remoteport = htons(netbuf_fromport(nbuf)); netconn_connect(socket->conn, netbuf_fromaddr(nbuf), netbuf_fromport(nbuf)); /* filesize <- -1 == unknown */ @@ -328,7 +326,7 @@ wait_pkt: switch (opcode) { case TFTP_ERROR: inode->size = 0; - break; /* ERROR reply; don't try again */ + goto done; /* ERROR reply; don't try again */ case TFTP_DATA: /* @@ -371,7 +369,7 @@ wait_pkt: socket->tftp_bytesleft = buffersize; socket->tftp_dataptr = socket->tftp_pktbuf; memcpy(socket->tftp_pktbuf, data, buffersize); - break; + goto done; case TFTP_OACK: /* @@ -443,11 +441,15 @@ wait_pkt: *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); if (!socket->tftp_pktbuf) goto err_reply; - break; + else + goto done; default: printf("TFTP unknown opcode %d\n", ntohs(opcode)); -- cgit v1.2.1 From f180d7c8ec74e0e15f1bcdf25c51899e2ca811c3 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 24 Apr 2011 20:55:47 -0700 Subject: pxe: use a separate poll thread instead of using an idle thread hook The idle thread can never sleep, so it's not really safe to do anything inside it. Instead, run a separate poll thread at low priority; we can also do that to poll the serial console if needed. Overall, the "classic" Syslinux idle handling really should go away and be replaced by the idle thread. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 46 +++++++++++++++++++++++++++------------------- core/fs/pxe/pxe.c | 10 +++++----- core/fs/pxe/pxe.h | 2 +- 3 files changed, 33 insertions(+), 25 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index ade3b0d2..1df758ce 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -12,7 +12,7 @@ extern uint8_t pxe_irq_pending; static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); -static struct thread *pxe_thread; +static struct thread *pxe_thread, *poll_thread; bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) { @@ -72,16 +72,6 @@ static void pxe_poll_wakeups(void) } } -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_process_irq(void) { static __lowmem t_PXENV_UNDI_ISR isr; @@ -128,14 +118,29 @@ static void pxe_receive_thread(void *dummy) for (;;) { sem_down(&pxe_receive_thread_sem, 0); - if (pxe_irq_vector || pxe_isr_poll()) - pxe_process_irq(); + pxe_process_irq(); } } -static void pxe_do_isr_poll(void) +static bool pxe_isr_poll(void) { - sem_up(&pxe_receive_thread_sem); + 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; + + for (;;) { + thread_yield(); + if (pxe_isr_poll()) + sem_up(&pxe_receive_thread_sem); + } } void pxe_init_isr(void) @@ -153,8 +158,9 @@ void pxe_init_isr(void) core_pm_hook = __schedule; if (!pxe_irq_vector) { - /* No IRQ vector, need to poll */ - idle_hook = pxe_do_isr_poll; + /* No IRQ vector, need to poll. */ + poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, + pxe_poll_thread, NULL); } } @@ -163,12 +169,14 @@ void pxe_cleanup_isr(void) { static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; - idle_hook = NULL; 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); - uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); + if (pxe_irq_vector) + uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); + else + kill_thread(poll_thread); } diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 2bcb27f5..858f573b 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -378,7 +378,7 @@ static void __pxe_searchdir(const char *filename, struct file *file) struct fs_info *fs = file->fs; struct inode *inode; char fullpath[2*FILENAME_MAX]; -#ifdef GPXE +#if GPXE char urlsave[2*FILENAME_MAX]; #endif struct url_info url; @@ -393,13 +393,13 @@ static void __pxe_searchdir(const char *filename, struct file *file) break; strlcpy(fullpath, filename, sizeof fullpath); -#ifdef GPXE +#if GPXE strcpy(urlsave, fullpath); #endif parse_url(&url, fullpath); if (url.type == URL_SUFFIX) { snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename); -#ifdef GPXE +#if GPXE strcpy(urlsave, fullpath); #endif parse_url(&url, fullpath); @@ -424,12 +424,12 @@ static void __pxe_searchdir(const char *filename, struct file *file) /* filename here is set on a redirect */ } -#ifdef GPXE if (!found_scheme) { +#if GPXE /* No URL scheme found, hand it to GPXE */ gpxe_open(inode, urlsave); - } #endif + } if (inode->size) file->inode = inode; diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index a685d8eb..24a8642a 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -242,7 +242,7 @@ void tftp_open(struct url_info *url, struct inode *inode, const char **redir); /* gpxeurl.c */ void gpxe_open(struct inode *inode, const char *url); -#define GPXE 1 +#define GPXE 0 /* http.c */ void http_open(struct url_info *url, struct inode *inode, const char **redir); -- cgit v1.2.1 From 9ca79e92e60dd243c60e257a2dc60ba7b3677f37 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 25 Apr 2011 11:46:56 -0700 Subject: PXE Cleanups, allow for 128 open files Clean up dead code in the PXE stack. The buffer assignment no longer limits the number of open files either, so raise it to something more than reasonable. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.h | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 24a8642a..8228defb 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -21,27 +21,15 @@ #define PXE_H #include -#include "fs.h" /* For MAX_OPEN, should go away */ +#include "fs.h" /* Mostly for FILENAME_MAX */ /* * Some basic defines... */ -#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'); -} - #define BOOTP_OPTION_MAGIC htonl(0x63825363) #define MAC_MAX 32 -- cgit v1.2.1 From 05fc41096ac07358660e96e138890999bcab89df Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 25 Apr 2011 16:37:24 -0700 Subject: core: dynamically size the heap Dynamically size the heap to 1/16 of available high memory. Signed-off-by: H. Peter Anvin --- core/fs/fs.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'core/fs') diff --git a/core/fs/fs.c b/core/fs/fs.c index ad2fb370..54099860 100644 --- a/core/fs/fs.c +++ b/core/fs/fs.c @@ -429,9 +429,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] = '/'; -- cgit v1.2.1 From 6bc5f3b0c7beb4ab445b6d4c7f1f18fcd0cb722e Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 25 Apr 2011 16:45:50 -0700 Subject: core, diskio: allocate the disk cache from the heap Since network buffers are allocated from the heap, and the heap is now dynamically sized, it makes sense we allocate the disk cache here, too. Signed-off-by: H. Peter Anvin --- core/fs/diskio.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'core/fs') diff --git a/core/fs/diskio.c b/core/fs/diskio.c index 66838161..4588d4d2 100644 --- a/core/fs/diskio.c +++ b/core/fs/diskio.c @@ -7,6 +7,7 @@ #include #include #include +#include #define RETRY_COUNT 6 @@ -396,24 +397,21 @@ 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]; + extern size_t HighMemSize, MallocStart; dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack, MaxTransfer); - dev.cache_data = diskcache; - dev.cache_size = sizeof diskcache; + dev.cache_size = min(128*1024, (MallocStart-HighMemSize) >> 1); + dev.cache_data = malloc(dev.cache_size); return &dev; } -- cgit v1.2.1 From 6e57f2115b84c073ccc94de55cf0e7367335c422 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 25 Apr 2011 17:30:59 -0700 Subject: pxe: remove non-lwIP definition of DNS_MAX_SERVERS There is only need for one definition of DNS_MAX_SERVERS, and it needs to be the one in lwIP, so use it everywhere. Furthermore, there is no longer any need for us to define DNS_MAX_PACKET. Signed-off-by: H. Peter Anvin --- core/fs/pxe/dhcp_option.c | 1 + core/fs/pxe/pxe.c | 1 + core/fs/pxe/pxe.h | 4 ---- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c index 47031faf..68f1ebe0 100644 --- a/core/fs/pxe/dhcp_option.c +++ b/core/fs/pxe/dhcp_option.c @@ -2,6 +2,7 @@ #include #include #include +#include /* DNS_MAX_SERVERS */ #include "pxe.h" char LocalDomain[256]; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 858f573b..7ac3519b 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "pxe.h" #include "thread.h" #include "url.h" diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 8228defb..94bc58d7 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -33,10 +33,6 @@ #define BOOTP_OPTION_MAGIC htonl(0x63825363) #define MAC_MAX 32 -/* Defines for DNS */ -#define DNS_MAX_PACKET 512 /* Defined by protocol */ -#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */ - /* * structures */ -- cgit v1.2.1 From 3953ca3532ca3281cc248f9985d9276c6f8486f0 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 25 Apr 2011 20:08:32 -0700 Subject: Generalize ipappend handling as "sysappend", and move to PM code Generalize the ipappend handling to cover all the derivatives, and rename it "sysappend" ("ipappend" is a valid alias for all derivatives.) Move all the string handling to protected mode. Currently only pxelinux exports strings, but the plan is to change that in the future. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 53 ++++++++++++++++++----------------------------------- core/fs/pxe/pxe.h | 3 --- 2 files changed, 18 insertions(+), 38 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 7ac3519b..c1b84766 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -20,14 +20,8 @@ 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; @@ -550,14 +544,14 @@ static int pxe_load_config(void) config_file = stpcpy(ConfigName, cfgprefix); /* Try loading by UUID */ - if (have_uuid) { - strcpy(config_file, UUID_str); + if (sysappend_strings[SYSAPPEND_UUID]) { + strcpy(config_file, sysappend_strings[SYSAPPEND_UUID]+8); if (try_load(ConfigName)) return 0; } /* Try loading by MAC address */ - strcpy(config_file, MAC_str); + strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7); if (try_load(ConfigName)) return 0; @@ -586,30 +580,32 @@ static int pxe_load_config(void) */ 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++); + + sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str; } /* * Generate the SYSUUID string, if we have one... */ static void make_sysuuid_string(void) { + static char sysuuid_str[8+32+5]; 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="); + dst = stpcpy(sysuuid_str, "SYSUUID="); while (*uuid_ptr) { int len = *uuid_ptr; @@ -622,6 +618,8 @@ static void make_sysuuid_string(void) } /* Remove last dash and zero-terminate */ *--dst = '\0'; + + sysappend_strings[SYSAPPEND_UUID] = sysuuid_str; } } @@ -630,21 +628,22 @@ static void make_sysuuid_string(void) * option into IPOption based on a DHCP packet in trackbuf. * */ -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; } @@ -652,6 +651,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); @@ -660,23 +660,6 @@ static void ip_init(void) printf("My IP address seems to be %08X %s\n", ip, dot_quad_buf); } -/* - * Print the IPAPPEND strings, in order - */ -extern const uint16_t IPAppends[]; -extern const char numIPAppends[]; - -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. @@ -956,7 +939,7 @@ static void network_init(void) make_bootif_string(); make_sysuuid_string(); ip_init(); - print_ipappend(); + print_sysappend(); /* * Check to see if we got any PXELINUX-specific DHCP options; in particular, diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 94bc58d7..d3e74996 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -155,9 +155,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; -- cgit v1.2.1 From 80a29a87118bd7539e8a310810dc0045d7a27279 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 25 Apr 2011 21:46:55 -0700 Subject: pxe, http: send the sysappend/ipappend strings as cookies When using http, send the sysappend/ipappend strings as cookies prefixed with _Syslinux_ ... the implementation of this may change. This is using the "old" cookie format for compactness (single header, for all cookies, and no $Version tag.) PHP at least seems perfectly happy to deal with it. Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index f46cd4cf..ae5e010c 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -1,3 +1,4 @@ +#include #include #include #include "pxe.h" @@ -45,6 +46,83 @@ static bool append_ch(char *str, size_t size, size_t *pos, int ch) return success; } +static size_t cookie_len; +static char *cookie_buf; + +static size_t http_do_bake_cookies(char *q) +{ + static const char uchexchar[16] = "0123456789ABCDEF"; + int i; + size_t len; + size_t n = 0; + const char *p; + char c; + size_t qlen = q ? -1UL : 0; + bool first = true; + + for (i = 0; i < SYSAPPEND_MAX; i++) { + if ((p = sysappend_strings[i])) { + len = snprintf(q, qlen, "%s_Syslinux_", first ? "Cookie: " : ""); + if (q) + q += len; + n += len; + first = false; + /* 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++; + } + } + 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) + return; + + http_do_bake_cookies(cookie_buf); +} + void http_open(struct url_info *url, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); @@ -73,6 +151,10 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) int status; int pos; + /* XXX: make this an external call at the appropriate time instead */ + if (!cookie_buf) + http_bake_cookies(); + socket->fill_buffer = tcp_fill_buffer; socket->close = tcp_close_file; @@ -107,12 +189,15 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) "Host: %s\r\n" "User-Agent: PXELINUX/%s\r\n" "Connection: close\r\n" + "%s" "\r\n", - url->host, VERSION_STR); + url->host, VERSION_STR, + cookie_buf ? cookie_buf : ""); if (header_len > sizeof header_buf) goto fail; /* Buffer overflow */ - err = netconn_write(socket->conn, header_buf, header_len, NETCONN_NOCOPY); + err = netconn_write(socket->conn, header_buf, + header_len, NETCONN_NOCOPY); if (err) { printf("netconn_write error %d\n", err); goto fail; -- cgit v1.2.1 From b6401220c0bf7482ad5b204d08ba831a7cf9eabb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 26 Apr 2011 14:41:31 -0700 Subject: Additional sysappend strings from DMI; pre-bake the http cookies - Add additional sysappend strings from DMI; we may want to add even more but let's think about it first. - Pre-generate http cookies. - Add a "sendcookies" command to mask out some of the information. Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 61 +++++++++++++++++------------ core/fs/pxe/pxe.c | 111 ++++++----------------------------------------------- core/fs/pxe/pxe.h | 1 + 3 files changed, 50 insertions(+), 123 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index ae5e010c..d98cc4ac 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -46,8 +46,10 @@ static bool append_ch(char *str, size_t size, size_t *pos, int ch) return success; } -static size_t cookie_len; -static char *cookie_buf; +static size_t cookie_len, header_len; +static char *cookie_buf, *header_buf; + +extern uint32_t SendCookies; static size_t http_do_bake_cookies(char *q) { @@ -59,9 +61,10 @@ static size_t http_do_bake_cookies(char *q) char c; size_t qlen = q ? -1UL : 0; bool first = true; + uint32_t mask = SendCookies; for (i = 0; i < SYSAPPEND_MAX; i++) { - if ((p = sysappend_strings[i])) { + if ((mask & 1) && (p = sysappend_strings[i])) { len = snprintf(q, qlen, "%s_Syslinux_", first ? "Cookie: " : ""); if (q) q += len; @@ -96,6 +99,7 @@ static size_t http_do_bake_cookies(char *q) *q++ = ';'; n++; } + mask >>= 1; } if (!first) { if (q) { @@ -117,8 +121,20 @@ void http_bake_cookies(void) cookie_len = http_do_bake_cookies(NULL); cookie_buf = malloc(cookie_len+1); - if (!cookie_buf) + 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); } @@ -126,8 +142,7 @@ void http_bake_cookies(void) void http_open(struct url_info *url, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); - char header_buf[4096]; - int header_len; + int header_bytes; const char *next; char field_name[20]; char field_value[1024]; @@ -151,9 +166,8 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) int status; int pos; - /* XXX: make this an external call at the appropriate time instead */ - if (!cookie_buf) - http_bake_cookies(); + if (!header_buf) + return; /* http is broken... */ socket->fill_buffer = tcp_fill_buffer; socket->close = tcp_close_file; @@ -178,26 +192,25 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) } strcpy(header_buf, "GET /"); - header_len = 5; - header_len += url_escape_unsafe(header_buf+5, url->path, + header_bytes = 5; + header_bytes += url_escape_unsafe(header_buf+5, url->path, sizeof header_buf - 5); - if (header_len > sizeof header_buf) + if (header_bytes > header_len) goto fail; /* Buffer overflow */ - header_len += snprintf(header_buf + header_len, - sizeof header_buf - header_len, - " HTTP/1.0\r\n" - "Host: %s\r\n" - "User-Agent: PXELINUX/%s\r\n" - "Connection: close\r\n" - "%s" - "\r\n", - url->host, VERSION_STR, - cookie_buf ? cookie_buf : ""); - if (header_len > sizeof header_buf) + header_bytes += snprintf(header_buf + header_bytes, + header_len - header_bytes, + " HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: PXELINUX/" VERSION_STR "\r\n" + "Connection: close\r\n" + "%s" + "\r\n", + url->host, cookie_buf ? cookie_buf : ""); + if (header_bytes > sizeof header_buf) goto fail; /* Buffer overflow */ err = netconn_write(socket->conn, header_buf, - header_len, NETCONN_NOCOPY); + header_bytes, NETCONN_NOCOPY); if (err) { printf("netconn_write error %d\n", err); goto fail; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index c1b84766..4bf8d006 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -77,50 +77,6 @@ static void pxe_close_file(struct file *file) 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; - } -} - /* * 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 @@ -149,27 +105,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; + 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]); } /* @@ -544,8 +484,8 @@ static int pxe_load_config(void) config_file = stpcpy(ConfigName, cfgprefix); /* Try loading by UUID */ - if (sysappend_strings[SYSAPPEND_UUID]) { - strcpy(config_file, sysappend_strings[SYSAPPEND_UUID]+8); + if (sysappend_strings[SYSAPPEND_SYSUUID]) { + strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8); if (try_load(ConfigName)) return 0; } @@ -556,7 +496,7 @@ static int pxe_load_config(void) 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 */ @@ -592,36 +532,6 @@ static void make_bootif_string(void) sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str; } -/* - * Generate the SYSUUID string, if we have one... - */ -static void make_sysuuid_string(void) -{ - static char sysuuid_str[8+32+5]; - 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; - - /* Try loading by UUID */ - if (have_uuid) { - dst = stpcpy(sysuuid_str, "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_UUID] = sysuuid_str; - } -} /* * Generate an ip=::: @@ -937,8 +847,11 @@ static void network_init(void) printf("\n"); 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(); + http_bake_cookies(); print_sysappend(); /* diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index d3e74996..0200c1e9 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -227,6 +227,7 @@ void gpxe_open(struct inode *inode, const char *url); /* http.c */ void http_open(struct url_info *url, struct inode *inode, const char **redir); +void http_bake_cookies(void); /* ftp.c */ void ftp_open(struct url_info *url, struct inode *inode, const char **redir); -- cgit v1.2.1 From 99e8e9b8043bc723486f5da22ceb640e2a340457 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 26 Apr 2011 15:23:43 -0700 Subject: pxe, http: correct the header overflow test sizeof header_buf isn't very useful when that's an actual pointer... Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index d98cc4ac..c7613bb6 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -185,6 +185,7 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) addr.addr = url->ip; if (!url->port) url->port = HTTP_PORT; + err = netconn_connect(socket->conn, &addr, url->port); if (err) { printf("netconn_connect error %d\n", err); @@ -194,7 +195,7 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) strcpy(header_buf, "GET /"); header_bytes = 5; header_bytes += url_escape_unsafe(header_buf+5, url->path, - sizeof header_buf - 5); + sizeof header_buf - 5); if (header_bytes > header_len) goto fail; /* Buffer overflow */ header_bytes += snprintf(header_buf + header_bytes, @@ -206,7 +207,7 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) "%s" "\r\n", url->host, cookie_buf ? cookie_buf : ""); - if (header_bytes > sizeof header_buf) + if (header_bytes > header_len) goto fail; /* Buffer overflow */ err = netconn_write(socket->conn, header_buf, -- cgit v1.2.1 From 3560c7519efefc126a5a5e24de079ed91b5745c2 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 26 Apr 2011 15:27:08 -0700 Subject: pxe, http: Use Syslinux/ as the User-agent: Use Syslinux as the User-agent name. Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index c7613bb6..75b3d3f4 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -202,7 +202,7 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) header_len - header_bytes, " HTTP/1.0\r\n" "Host: %s\r\n" - "User-Agent: PXELINUX/" VERSION_STR "\r\n" + "User-Agent: Syslinux/" VERSION_STR "\r\n" "Connection: close\r\n" "%s" "\r\n", -- cgit v1.2.1 From d5cf286f02295c76978f365b9f5d6b848e6c8d4c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 26 Apr 2011 17:37:52 -0700 Subject: pxe, http: simplify cookie generation, fix boundary conditions Simplify the code to create the cookie header, fix buffer size boundary conditions. Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index 75b3d3f4..a3928ba8 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -59,17 +59,24 @@ static size_t http_do_bake_cookies(char *q) size_t n = 0; const char *p; char c; - size_t qlen = q ? -1UL : 0; bool first = true; uint32_t mask = SendCookies; for (i = 0; i < SYSAPPEND_MAX; i++) { if ((mask & 1) && (p = sysappend_strings[i])) { - len = snprintf(q, qlen, "%s_Syslinux_", first ? "Cookie: " : ""); - if (q) - q += len; - n += len; - first = false; + 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++; @@ -195,8 +202,8 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) strcpy(header_buf, "GET /"); header_bytes = 5; header_bytes += url_escape_unsafe(header_buf+5, url->path, - sizeof header_buf - 5); - if (header_bytes > header_len) + header_len - 5); + if (header_bytes >= header_len) goto fail; /* Buffer overflow */ header_bytes += snprintf(header_buf + header_bytes, header_len - header_bytes, @@ -207,7 +214,7 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) "%s" "\r\n", url->host, cookie_buf ? cookie_buf : ""); - if (header_bytes > header_len) + if (header_bytes >= header_len) goto fail; /* Buffer overflow */ err = netconn_write(socket->conn, header_buf, -- cgit v1.2.1 From 92bc1767bbb308679c69486e60aa972c5c06931d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 26 Apr 2011 17:39:35 -0700 Subject: pxe: start the poll thread when we would have started the ISR Start the poll thread when we would have started the ISR, i.e. when enabling the interface. Otherwise we might poll a stack that isn't yet initialized. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 18 +++++++++++++----- core/fs/pxe/pxe.h | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 1df758ce..1f79e782 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -14,7 +14,7 @@ extern uint8_t pxe_irq_pending; static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); static struct thread *pxe_thread, *poll_thread; -bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) +static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) { far_ptr_t *entry; unsigned int vec; @@ -143,6 +143,9 @@ static void pxe_poll_thread(void *dummy) } } +/* + * This does preparations and enables the PXE thread + */ void pxe_init_isr(void) { start_idle_thread(); @@ -156,15 +159,20 @@ void pxe_init_isr(void) pxe_thread = start_thread("pxe receive", 16384, -20, pxe_receive_thread, NULL); core_pm_hook = __schedule; +} - if (!pxe_irq_vector) { - /* No IRQ vector, need to poll. */ +/* + * Actually start the interrupt routine inside the UNDI stack + */ +void pxe_start_isr(void) +{ + if (pxe_irq_vector) + install_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); + else poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, pxe_poll_thread, NULL); - } } - void pxe_cleanup_isr(void) { static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 0200c1e9..c8d35d0b 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -193,8 +193,8 @@ extern far_ptr_t pxe_irq_chain; /* isr.c */ void pxe_init_isr(void); +void pxe_start_isr(void); void pxe_cleanup_isr(void); -bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old); /* pxe.c */ struct url_info; -- cgit v1.2.1 From ecc3cb5682637f375c6733e38e0a3220c2793739 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 26 Apr 2011 17:41:20 -0700 Subject: pxe: minor initialization cleanups Remove duplicate or dead code, move http_bake_cookies() to a more logical place. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 4bf8d006..3242a9f5 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -851,8 +851,8 @@ static void network_init(void) if (have_uuid) sysappend_set_uuid(uuid); ip_init(); - http_bake_cookies(); print_sysappend(); + http_bake_cookies(); /* * Check to see if we got any PXELINUX-specific DHCP options; in particular, @@ -873,12 +873,6 @@ static void network_init(void) for (i = 0; i < DNS_MAX_SERVERS; i++) { /* Transfer the DNS information to lwip */ - dprintf("DNS server %d = %d.%d.%d.%d\n", - i, - ((uint8_t *)&dns_server[i])[0], - ((uint8_t *)&dns_server[i])[1], - ((uint8_t *)&dns_server[i])[2], - ((uint8_t *)&dns_server[i])[3]); dns_setserver(i, (struct ip_addr *)&dns_server[i]); } } @@ -898,10 +892,6 @@ static int pxe_fs_init(struct fs_info *fs) fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2; fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2; - /* 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; - /* Find the PXE stack */ if (pxe_init(false)) kaboom(); -- cgit v1.2.1 From 7a97bd6adda1d2bc270a33d8ec774e4c5eb88056 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 1 May 2011 18:04:38 -0700 Subject: pxe, http: Remove unused variable Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 1 - 1 file changed, 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index a3928ba8..fef87b0a 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -55,7 +55,6 @@ static size_t http_do_bake_cookies(char *q) { static const char uchexchar[16] = "0123456789ABCDEF"; int i; - size_t len; size_t n = 0; const char *p; char c; -- cgit v1.2.1 From 6ed325a3c881565cc2473d1fbfa69d806b4b7bbf Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 1 May 2011 18:11:40 -0700 Subject: core: pass the file flags down through the stack Pass the file flags down through the stack. This allows us to distinguish between open for read, open for write, or opendir in the low-level filesystem functions; this will matter for the PXE methods. Signed-off-by: H. Peter Anvin --- core/fs/chdir.c | 3 ++- core/fs/fs.c | 13 +++++++------ core/fs/pxe/pxe.c | 10 ++++++---- core/fs/readdir.c | 3 ++- 4 files changed, 17 insertions(+), 12 deletions(-) (limited to 'core/fs') diff --git a/core/fs/chdir.c b/core/fs/chdir.c index 9e8dfd2e..0df0286b 100644 --- a/core/fs/chdir.c +++ b/core/fs/chdir.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "fs.h" #include "cache.h" @@ -79,7 +80,7 @@ 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/fs.c b/core/fs/fs.c index 54099860..81dafe9d 100644 --- a/core/fs/fs.c +++ b/core/fs/fs.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "fs.h" #include "cache.h" @@ -186,7 +187,7 @@ void pm_searchdir(com32sys_t *regs) char *name = MK_PTR(regs->ds, regs->edi.w[0]); int rv; - rv = searchdir(name); + rv = searchdir(name, O_RDONLY); if (rv < 0) { regs->esi.w[0] = 0; regs->eax.l = 0; @@ -198,7 +199,7 @@ void pm_searchdir(com32sys_t *regs) } } -int searchdir(const char *name) +int searchdir(const char *name, int flags) { struct inode *inode = NULL; struct inode *parent = NULL; @@ -213,7 +214,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); @@ -343,14 +344,14 @@ err_no_close: return -1; } -int open_file(const char *name, struct com32_filedata *filedata) +int open_file(const char *name, int flags, struct com32_filedata *filedata) { int rv; struct file *file; char mangled_name[FILENAME_MAX]; mangle_name(mangled_name, name); - rv = searchdir(mangled_name); + rv = searchdir(mangled_name, flags); if (rv < 0) return rv; @@ -377,7 +378,7 @@ void pm_open_file(com32sys_t *regs) char mangled_name[FILENAME_MAX]; mangle_name(mangled_name, name); - rv = searchdir(mangled_name); + rv = searchdir(mangled_name, O_RDONLY); if (rv < 0) { regs->eflags.l |= EFLAGS_CF; } else { diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 3242a9f5..b7ec0506 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -295,20 +295,20 @@ static void url_set_ip(struct url_info *url) * @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; @@ -321,6 +321,8 @@ static void __pxe_searchdir(const char *filename, struct file *file) int redirect_count = 0; bool found_scheme = false; + (void)flags; + inode = file->inode = NULL; while (filename) { diff --git a/core/fs/readdir.c b/core/fs/readdir.c index d071affd..a437011d 100644 --- a/core/fs/readdir.c +++ b/core/fs/readdir.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -12,7 +13,7 @@ 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; -- cgit v1.2.1 From 275abcfb66c764bbc7b0d07ec9b63e6fda63ec3a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 1 May 2011 18:30:01 -0700 Subject: pxe: push the open flags down into individual methods Push the open flags down to the individual system methods. Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 5 ++++- core/fs/pxe/http.c | 5 ++++- core/fs/pxe/pxe.c | 18 ++++++++++-------- core/fs/pxe/pxe.h | 9 ++++++--- core/fs/pxe/tftp.c | 4 +++- 5 files changed, 27 insertions(+), 14 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 4efcedb6..93255a1f 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -168,7 +168,8 @@ static void ftp_close_file(struct inode *inode) ftp_free(inode); } -void ftp_open(struct url_info *url, struct inode *inode, const char **redir) +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; @@ -179,6 +180,8 @@ void ftp_open(struct url_info *url, struct inode *inode, const char **redir) err_t err; (void)redir; /* FTP does not redirect */ + (void)flags; + inode->size = 0; if (!url->port) diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index fef87b0a..6f159f6f 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -145,7 +145,8 @@ void http_bake_cookies(void) http_do_bake_cookies(cookie_buf); } -void http_open(struct url_info *url, struct inode *inode, const char **redir) +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; @@ -172,6 +173,8 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) int status; int pos; + (void)flags; + if (!header_buf) return; /* http is broken... */ diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index b7ec0506..60f5d581 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -32,13 +33,15 @@ __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); static struct url_scheme { const char *name; - void (*open)(struct url_info *url, struct inode *inode, const char **redir); + void (*open)(struct url_info *, int, struct inode *, const char **); + int ok_flags; } url_schemes[] = { - { "tftp", tftp_open }, - { "http", http_open }, - { "ftp", ftp_open }, - { NULL, NULL }, + { "tftp", tftp_open, 0 }, + { "http", http_open, O_DIRECTORY }, + { "ftp", ftp_open, 0 }, + { NULL, NULL, 0 }, }; +#define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY) /* * Allocate a local UDP port structure and assign it a local port number. @@ -321,8 +324,6 @@ static void __pxe_searchdir(const char *filename, int flags, struct file *file) int redirect_count = 0; bool found_scheme = false; - (void)flags; - inode = file->inode = NULL; while (filename) { @@ -352,7 +353,8 @@ static void __pxe_searchdir(const char *filename, int flags, struct file *file) found_scheme = false; for (us = url_schemes; us->name; us++) { if (!strcmp(us->name, url.scheme)) { - us->open(&url, inode, &filename); + if (((flags ^ us->ok_flags) & OK_FLAGS_MASK) == 0) + us->open(&url, flags, inode, &filename); found_scheme = true; break; } diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index c8d35d0b..c0847906 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -219,18 +219,21 @@ void pxe_idle_init(void); void pxe_idle_cleanup(void); /* tftp.c */ -void tftp_open(struct url_info *url, struct inode *inode, const char **redir); +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, struct inode *inode, const char **redir); +void http_open(struct url_info *url, int flags, struct inode *inode, + const char **redir); void http_bake_cookies(void); /* ftp.c */ -void ftp_open(struct url_info *url, struct inode *inode, const char **redir); +void ftp_open(struct url_info *url, int flags, struct inode *inode, + const char **redir); /* tcp.c */ void tcp_close_file(struct inode *inode); diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 5aafd8eb..b203a8f1 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -209,7 +209,8 @@ static void tftp_get_packet(struct inode *inode) * @out: the lenght of this file, stores in file->file_len * */ -void tftp_open(struct url_info *url, struct inode *inode, const char **redir) +void tftp_open(struct url_info *url, int flags, struct inode *inode, + const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); char *buf; @@ -234,6 +235,7 @@ void tftp_open(struct url_info *url, struct inode *inode, const char **redir) struct ip_addr addr; (void)redir; /* TFTP does not redirect */ + (void)flags; if (url->type != URL_OLD_TFTP) { /* -- cgit v1.2.1 From efd2d8b54c4fa38ae9a036d5bce72baca4c9cce1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 1 May 2011 18:40:45 -0700 Subject: pxe: move the per-connection-type operations into a structure Move the per-connection-type operations into a structure, to make it easier to modify them in the future. Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 16 ++++++++-------- core/fs/pxe/http.c | 4 ++-- core/fs/pxe/pxe.c | 6 +++--- core/fs/pxe/pxe.h | 9 +++++++-- core/fs/pxe/tcp.c | 7 ++++++- core/fs/pxe/tftp.c | 8 ++++++-- 6 files changed, 32 insertions(+), 18 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 93255a1f..0079e329 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -156,8 +156,6 @@ static void ftp_close_file(struct inode *inode) struct pxe_pvt_inode *ctlsock; int resp; - //printf("In ftp_close_file\n"); - ctlsock = socket->ctl ? PVT(socket->ctl) : NULL; if (ctlsock->conn) { resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL); @@ -168,6 +166,11 @@ static void ftp_close_file(struct inode *inode) ftp_free(inode); } +static const struct pxe_conn_ops ftp_conn_ops = { + .fill_buffer = tcp_fill_buffer, + .close = ftp_close_file, +}; + void ftp_open(struct url_info *url, int flags, struct inode *inode, const char **redir) { @@ -189,17 +192,14 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, url_unescape(url->path, 0); - socket->fill_buffer = tcp_fill_buffer; - socket->close = ftp_close_file; + socket->ops = &ftp_conn_ops; - /* Allocate a socket for the control connection */ + /* 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->fill_buffer = tcp_fill_buffer; - ctlsock->close = tcp_close_file; - + ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */ ctlsock->conn = netconn_new(NETCONN_TCP); if (!ctlsock->conn) goto err_free; diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index 6f159f6f..3f2bb15a 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -178,8 +178,8 @@ void http_open(struct url_info *url, int flags, struct inode *inode, if (!header_buf) return; /* http is broken... */ - socket->fill_buffer = tcp_fill_buffer; - socket->close = tcp_close_file; + /* This is a straightforward TCP connection after headers */ + socket->ops = &tcp_conn_ops; /* Reset all of the variables */ inode->size = content_length = -1; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 60f5d581..bac0706e 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -74,7 +74,7 @@ static void pxe_close_file(struct file *file) struct pxe_pvt_inode *socket = PVT(inode); if (!socket->tftp_goteof) { - socket->close(inode); + socket->ops->close(inode); } free_socket(inode); @@ -200,7 +200,7 @@ int pxe_getc(struct inode *inode) if (socket->tftp_goteof) return -1; - socket->fill_buffer(inode); + socket->ops->fill_buffer(inode); } byte = *socket->tftp_dataptr; @@ -220,7 +220,7 @@ static void fill_buffer(struct inode *inode) if (socket->tftp_bytesleft || socket->tftp_goteof) return; - return socket->fill_buffer(inode); + return socket->ops->fill_buffer(inode); } diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index c0847906..a3b6cb27 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -108,6 +108,11 @@ 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); +}; + struct pxe_pvt_inode { struct netconn *conn; /* lwip network connection */ struct netbuf *buf; /* lwip cached buffer */ @@ -121,8 +126,7 @@ struct pxe_pvt_inode { uint8_t tftp_unused[3]; /* Currently unused */ char *tftp_pktbuf; /* Packet buffer */ struct inode *ctl; /* Control connection (for FTP) */ - void (*fill_buffer)(struct inode *inode); - void (*close)(struct inode *inode); + const struct pxe_conn_ops *ops; }; #define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt)) @@ -238,5 +242,6 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, /* 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 index 759ec9f3..daff4d70 100644 --- a/core/fs/pxe/tcp.c +++ b/core/fs/pxe/tcp.c @@ -56,7 +56,7 @@ void tcp_fill_buffer(struct inode *inode) socket->tftp_goteof = 1; if (inode->size == -1) inode->size = socket->tftp_filepos; - socket->close(inode); + socket->ops->close(inode); return; } } @@ -71,3 +71,8 @@ void tcp_fill_buffer(struct inode *inode) 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 index b203a8f1..03c4e1f6 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -198,6 +198,11 @@ static void tftp_get_packet(struct inode *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 * @@ -248,8 +253,7 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, if (!url->port) url->port = TFTP_PORT; - socket->fill_buffer = tftp_get_packet; - socket->close = tftp_close_file; + socket->ops = &tftp_conn_ops; socket->conn = netconn_new(NETCONN_UDP); if (!socket->conn) return; -- cgit v1.2.1 From 6ae3556b679985e5ed880d0ebe75f3a095ab8d03 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 1 May 2011 20:51:04 -0700 Subject: pxe: remove a stray inline The gateway() inline is no longer used. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index a3b6cb27..65c693bb 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -174,18 +174,6 @@ extern uint8_t uuid[]; extern uint16_t BIOS_fbm; extern const uint8_t TimeoutTable[]; -/* - * Compute the suitable gateway for a specific route -- too many - * vendor PXE stacks don't do this correctly... - */ -static inline uint32_t gateway(uint32_t ip) -{ - if ((ip ^ IPInfo.myip) & IPInfo.netmask) - return IPInfo.gateway; - else - return 0; -} - /* * functions */ -- cgit v1.2.1 From 65f4305b9509a5168c8f11e5ccb4d3db55eac405 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 1 May 2011 21:15:22 -0700 Subject: pxe, tftp: remove global buffers, double buffering Remove the global packet_buf, and drop an unnecessary copy in TFTP receive. Change the tftp_lastpkt counter to host byte order, it really makes life easier. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 3 --- core/fs/pxe/pxe.h | 3 +-- core/fs/pxe/tftp.c | 53 +++++++++++++++++++++++++++++------------------------ 3 files changed, 30 insertions(+), 29 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index bac0706e..f723acbc 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -28,9 +28,6 @@ static bool has_gpxe; static uint32_t gpxe_funcs; bool have_uuid = false; -/* Common receive buffer */ -__lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); - static struct url_scheme { const char *name; void (*open)(struct url_info *, int, struct inode *, const char **); diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 65c693bb..6c84d8be 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -120,7 +120,7 @@ struct pxe_pvt_inode { 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 (NBO) */ + 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 */ @@ -192,7 +192,6 @@ void pxe_cleanup_isr(void); struct url_info; bool ip_ok(uint32_t); int pxe_call(int, void *); -extern __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16); int pxe_getc(struct inode *inode); void free_socket(struct inode *inode); diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 03c4e1f6..f1e22435 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -12,6 +12,11 @@ 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) + \ @@ -74,7 +79,7 @@ static void tftp_error(struct inode *inode, uint16_t errnum, * 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) + * @param: ack_num, Packet # to ack (host byte order) * */ static void ack_packet(struct inode *inode, uint16_t ack_num) @@ -86,7 +91,7 @@ static void ack_packet(struct inode *inode, uint16_t ack_num) /* Packet number to ack */ ack_packet_buf[0] = TFTP_ACK; - ack_packet_buf[1] = ack_num; + ack_packet_buf[1] = htons(ack_num); nbuf = netbuf_new(); netbuf_ref(nbuf, ack_packet_buf, 4); @@ -108,8 +113,9 @@ static void tftp_get_packet(struct inode *inode) const uint8_t *timeout_ptr; uint8_t timeout; uint16_t buffersize; + uint16_t serial; jiffies_t oldtime; - void *data = NULL; + struct tftp_packet *pkt = NULL; struct netbuf *nbuf; u16_t nbuf_len; struct pxe_pvt_inode *socket = PVT(inode); @@ -143,16 +149,16 @@ static void tftp_get_packet(struct inode *inode) netbuf_first(nbuf); nbuf_len = 0; nbuf_len = netbuf_len(nbuf); - if (nbuf_len <= PKTBUF_SIZE) - netbuf_copy(nbuf, packet_buf, nbuf_len); + if (nbuf_len <= socket->tftp_blksize + 4) + netbuf_copy(nbuf, socket->tftp_pktbuf, nbuf_len); else - nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ + nbuf_len = 0; /* invalid packet */ netbuf_delete(nbuf); - if (nbuf_len < 4) /* Bad size for a DATA packet */ + if (nbuf_len < 4) /* Bad size for a DATA packet */ continue; - data = packet_buf; - if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */ + pkt = (struct tftp_packet *)(socket->tftp_pktbuf); + if (pkt->opcode != TFTP_DATA) /* Not a data packet */ continue; /* If goes here, recevie OK, break */ @@ -164,10 +170,9 @@ static void tftp_get_packet(struct inode *inode) kaboom(); 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) { + serial = ntohs(pkt->serial); + if (serial != last_pkt) { /* * Wrong packet, ACK the packet and try again. * This is presumably because the ACK got lost, @@ -183,13 +188,12 @@ static void tftp_get_packet(struct inode *inode) /* It's the packet we want. We're also EOF if the size < blocksize */ socket->tftp_lastpkt = last_pkt; /* Update last packet number */ buffersize = nbuf_len - 4; /* Skip TFTP header */ - memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize); - socket->tftp_dataptr = socket->tftp_pktbuf; + 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, *(uint16_t *)(data + 2)); + ack_packet(inode, serial); /* Make sure we know we are at end of file */ inode->size = socket->tftp_filepos; @@ -225,7 +229,8 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, char *options; char *data; 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]; + 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; @@ -307,7 +312,7 @@ wait_pkt: ok_source = netbuf_fromaddr(nbuf)->addr == url->ip; nbuf_len = netbuf_len(nbuf); if (nbuf_len <= PKTBUF_SIZE) - netbuf_copy(nbuf, packet_buf, nbuf_len); + netbuf_copy(nbuf, reply_packet_buf, nbuf_len); else nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ netbuf_delete(nbuf); @@ -328,7 +333,7 @@ wait_pkt: /* * Get the opcode type, and parse it */ - opcode = *(uint16_t *)packet_buf; + opcode = *(uint16_t *)reply_packet_buf; switch (opcode) { case TFTP_ERROR: inode->size = 0; @@ -348,16 +353,16 @@ wait_pkt: buffersize -= 2; if (buffersize < 0) goto wait_pkt; - data = packet_buf + 2; - blk_num = *(uint16_t *)data; + data = reply_packet_buf + 2; + blk_num = ntohs(*(uint16_t *)data); data += 2; - if (blk_num != htons(1)) + 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); + socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4); if (!socket->tftp_pktbuf) goto err_reply; /* Internal error */ @@ -383,7 +388,7 @@ wait_pkt: * and packet sizes. */ - options = packet_buf + 2; + options = reply_packet_buf + 2; p = options; while (buffersize) { @@ -451,7 +456,7 @@ wait_pkt: goto err_reply; /* Parsing successful, allocate buffer */ - socket->tftp_pktbuf = malloc(socket->tftp_blksize); + socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4); if (!socket->tftp_pktbuf) goto err_reply; else -- cgit v1.2.1 From 2da6006eedc7a1481b669090dc112411fbf8b384 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 2 May 2011 22:16:18 -0700 Subject: pxe: fix the flags test Fix the test for permissible file mode flags. This unbreaks http. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index f723acbc..f54e595e 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -350,7 +350,7 @@ static void __pxe_searchdir(const char *filename, int flags, struct file *file) 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) + if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0) us->open(&url, flags, inode, &filename); found_scheme = true; break; -- cgit v1.2.1 From 40068cfcbf723c9363194bf20a80b80a53c249fd Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 2 May 2011 23:36:41 -0700 Subject: pxe, http: support readdir (ls) over http Use a heuristic http index parser (which is assumed to work with most webserver-generated indicies) to support ls over http. Signed-off-by: H. Peter Anvin --- core/fs/pxe/http.c | 8 +- core/fs/pxe/http_readdir.c | 471 +++++++++++++++++++++++++++++++++++++++++++++ core/fs/pxe/pxe.c | 18 +- core/fs/pxe/pxe.h | 4 + 4 files changed, 498 insertions(+), 3 deletions(-) create mode 100644 core/fs/pxe/http_readdir.c (limited to 'core/fs') diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index 3f2bb15a..1fd87aa6 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -145,6 +145,12 @@ void http_bake_cookies(void) 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) { @@ -179,7 +185,7 @@ void http_open(struct url_info *url, int flags, struct inode *inode, return; /* http is broken... */ /* This is a straightforward TCP connection after headers */ - socket->ops = &tcp_conn_ops; + socket->ops = &http_conn_ops; /* Reset all of the variables */ inode->size = content_length = -1; 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 +#include +#include +#include +#include +#include "pxe.h" + +enum http_readdir_state { + st_start, /* 0 Initial state */ + st_open, /* 1 "<" */ + st_a, /* 2 " */ + 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/pxe.c b/core/fs/pxe/pxe.c index f54e595e..acefac99 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -367,10 +367,12 @@ static void __pxe_searchdir(const char *filename, int flags, struct file *file) #endif } - if (inode->size) + if (inode->size) { file->inode = inode; - else + file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG; + } else { free_socket(inode); + } return; } @@ -1091,6 +1093,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, @@ -1102,4 +1115,5 @@ const struct fs_ops pxe_fs_ops = { .close_file = pxe_close_file, .mangle_name = pxe_mangle_name, .load_config = pxe_load_config, + .readdir = pxe_readdir, }; diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 6c84d8be..4c7e8e6e 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -111,6 +111,7 @@ struct netbuf; struct pxe_conn_ops { void (*fill_buffer)(struct inode *inode); void (*close)(struct inode *inode); + int (*readdir)(struct inode *inode, struct dirent *dirent); }; struct pxe_pvt_inode { @@ -222,6 +223,9 @@ void http_open(struct url_info *url, int flags, struct inode *inode, const char **redir); void http_bake_cookies(void); +/* 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); -- cgit v1.2.1 From 4c9b4b40238d6afda386d1d5fb088cbc9aa68c42 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 3 May 2011 23:37:10 -0700 Subject: pxe, ftp: support readdir for FTP Support readdir for FTP. This uses some heuristics for parsing LIST output which (hopefully) works on most systems... there is no hope for weird filenames with leading spaces or control characters, though. That would require MLSD support, which doesn't seem to be very common. Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 32 ++++++++--- core/fs/pxe/ftp_readdir.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++ core/fs/pxe/pxe.c | 2 +- core/fs/pxe/pxe.h | 3 + 4 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 core/fs/pxe/ftp_readdir.c (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 0079e329..10a9f5c4 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -1,16 +1,28 @@ +/* ----------------------------------------------------------------------- * + * + * 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 #include #include -#include -#include +#include #include #include #include #include +#include "core.h" +#include "fs.h" #include "pxe.h" #include "thread.h" #include "url.h" @@ -169,6 +181,7 @@ static void ftp_close_file(struct inode *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, @@ -183,7 +196,6 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, err_t err; (void)redir; /* FTP does not redirect */ - (void)flags; inode->size = 0; @@ -229,9 +241,11 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, goto err_disconnect; } - resp = ftp_cmd_response(socket->ctl, "TYPE", "I", NULL, NULL); - if (resp != 200) - 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); //printf("%u PASV %u bytes %u,%u,%u,%u,%u,%u\n", @@ -248,7 +262,9 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, if (err) goto err_disconnect; - resp = ftp_cmd_response(socket->ctl, "RETR", url->path, NULL, NULL); + resp = ftp_cmd_response(socket->ctl, + (flags & O_DIRECTORY) ? "LIST" : "RETR", + url->path, NULL, NULL); if (resp != 125 && resp != 150) goto err_disconnect; diff --git a/core/fs/pxe/ftp_readdir.c b/core/fs/pxe/ftp_readdir.c new file mode 100644 index 00000000..2eb7748c --- /dev/null +++ b/core/fs/pxe/ftp_readdir.c @@ -0,0 +1,140 @@ +/* ----------------------------------------------------------------------- * + * + * 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 +#include +#include +#include +#include +#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 '-': + 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, "")) { + /* 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/pxe.c b/core/fs/pxe/pxe.c index acefac99..267b32fb 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -35,7 +35,7 @@ static struct url_scheme { } url_schemes[] = { { "tftp", tftp_open, 0 }, { "http", http_open, O_DIRECTORY }, - { "ftp", ftp_open, 0 }, + { "ftp", ftp_open, O_DIRECTORY }, { NULL, NULL, 0 }, }; #define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY) diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 4c7e8e6e..47c233f6 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -230,6 +230,9 @@ int http_readdir(struct inode *inode, struct dirent *dirent); 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); -- cgit v1.2.1 From b61066d7577ddbe1283a973df668bb124d3ddb23 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 3 May 2011 23:54:58 -0700 Subject: pxe, ftp: correct the handling of continuation lines Correct the handling of FTP messages with continuation lines. Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 10a9f5c4..097536d1 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -80,19 +80,16 @@ static int ftp_cmd_response(struct inode *inode, const char *cmd, done = false; while ((c = pxe_getc(inode)) >= 0) { - //printf("%c", c); - if (c == '\n') { - pos = 0; if (done) { if (pn) { pn += ps; if (pn_ptr) *pn_ptr = pn; } - //printf("FTP response: %u\n", code); return code; } + pos = code = 0; first_line = false; continue; } @@ -248,9 +245,6 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, } resp = ftp_cmd_response(socket->ctl, "PASV", NULL, pasv_data, &pasv_bytes); - //printf("%u PASV %u bytes %u,%u,%u,%u,%u,%u\n", - //resp, pasv_bytes, pasv_data[0], pasv_data[1], pasv_data[2], - //pasv_data[3], pasv_data[4], pasv_data[5]); if (resp != 227 || pasv_bytes != 6) goto err_disconnect; -- cgit v1.2.1 From 50629fda7e1ff723c8e72d0b726693be9f0e9970 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 May 2011 08:15:54 -0700 Subject: pxe, dns: add the local domain to unqualified hostnames For unqualified hostnames (lacking dot), add the local hostname. This matches previous PXELINUX behavior. Arguably we should really implement the whole domain search stuff. Reported-by: Gene Cumm Signed-off-by: H. Peter Anvin --- core/fs/pxe/dnsresolv.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c index 350dfe31..8dc17939 100644 --- a/core/fs/pxe/dnsresolv.c +++ b/core/fs/pxe/dnsresolv.c @@ -82,15 +82,17 @@ static bool parse_dotquad(const char *ip_str, uint32_t *res) } /* - * 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. + * + * 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 */ uint32_t dns_resolv(const char *name) { err_t err; struct ip_addr ip; + char fullname[512]; /* * Return failure on an empty input... this can happen during @@ -108,6 +110,12 @@ uint32_t dns_resolv(const char *name) if (!dns_getserver(0).addr) return 0; + /* Is it a local (unqualified) domain name? */ + if (!strchr(name, '.') && LocalDomain[0]) { + snprintf(fullname, sizeof fullname, "%s.%s", name, LocalDomain); + name = fullname; + } + err = netconn_gethostbyname(name, &ip); if (err) return 0; -- cgit v1.2.1 From 28cbed47f60a153e38674727a4cdd8a42c268583 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 8 May 2011 16:48:10 -0700 Subject: pxe, ftp: handle some DOS FTP stacks correctly Handle stacks which output similar to DOS DIR format correctly. Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp_readdir.c | 1 + 1 file changed, 1 insertion(+) (limited to 'core/fs') diff --git a/core/fs/pxe/ftp_readdir.c b/core/fs/pxe/ftp_readdir.c index 2eb7748c..6b87f77e 100644 --- a/core/fs/pxe/ftp_readdir.c +++ b/core/fs/pxe/ftp_readdir.c @@ -32,6 +32,7 @@ static int dirtype(char type) case 'b': return DT_BLK; case '-': + case '0' ... '9': /* Some DOS FTP stacks */ return DT_REG; case 'l': return DT_LNK; -- cgit v1.2.1 From 96a59825a49f72f99ac92702d08bb4b91202bab7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 8 May 2011 18:29:18 -0700 Subject: pxe: try to mimic the policy of iPXE with respect to interrupts From the iPXE checkin message: [undi] Cope with devices that erroneously claim not to use interrupts Some PXE stacks advertise that interrupts are not supported, despite requiring the use of interrupts. Attempt to cope with such cards without breaking others by always hooking the interrupt, and using the "interrupts supported" flag only to decide whether or not to wait for an interrupt before calling PXENV_UNDI_ISR_IN_PROCESS. The possible combinations are therefore: 1. Card generates interrupts and claims to support interrupts iPXE will call PXENV_UNDI_ISR_IN_PROCESS only after an interrupt has been observed. (This is required to avoid lockups in some PXE stacks, which spuriously sulk if called before an interrupt has been generated.) Such a card should work correctly. 2. Card does not generate interrupts and does not claim to support interrupts iPXE will call PXENV_UNDI_ISR_IN_PROCESS indiscriminately, matching the observed behaviour of at least one other PXE NBP (winBoot/i). Such a card should work correctly. 3. Card generates interrupts but claims not to support interrupts iPXE will call PXENV_UNDI_ISR_IN_PROCESS indiscriminately. An interrupt will still result in a call to PXENV_UNDI_ISR_IN_START. Such a card may work correctly. 4. Card does not generate interrupts but claims to support interrupts Such a card will not work at all. This attempts to match this policy. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 20 ++++++++++++++------ core/fs/pxe/pxe.c | 14 +++++++++++++- core/fs/pxe/pxe.h | 3 +++ 3 files changed, 30 insertions(+), 7 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 1f79e782..bbad372e 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -29,6 +29,10 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) entry = (far_ptr_t *)(vec << 2); *old = *entry; entry->ptr = (uint32_t)isr; + + printf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec, + old->seg, old->offs, entry->seg, entry->offs); + return true; } @@ -166,9 +170,13 @@ void pxe_init_isr(void) */ void pxe_start_isr(void) { - if (pxe_irq_vector) - install_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); - else + pxe_irq_vector = pxe_undi_info.IntNumber; /* Used in 16-bit code */ + + if (pxe_undi_info.IntNumber) + install_irq_vector(pxe_undi_info.IntNumber, pxe_isr, &pxe_irq_chain); + + if (!pxe_undi_info.IntNumber || + !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, pxe_poll_thread, NULL); } @@ -183,8 +191,8 @@ void pxe_cleanup_isr(void) memset(&undi_close, 0, sizeof(undi_close)); pxe_call(PXENV_UNDI_CLOSE, &undi_close); - if (pxe_irq_vector) - uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); - else + if (pxe_undi_info.IntNumber) + uninstall_irq_vector(pxe_undi_info.IntNumber, pxe_isr, &pxe_irq_chain); + if (poll_thread) kill_thread(poll_thread); } diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 267b32fb..867404ee 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -15,6 +15,9 @@ #include "url.h" #include "tftp.h" +__lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info; +__lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface; + static uint16_t real_base_mem; /* Amount of DOS memory after freeing */ uint8_t MAC[MAC_MAX]; /* Actual MAC address */ @@ -774,6 +777,15 @@ static int pxe_init(bool quiet) real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */ + /* Probe UNDI information */ + pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info); + pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface); + + printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n", + pxe_undi_info.BaseIo, pxe_undi_info.IntNumber, + pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType, + pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags); + return 0; } @@ -854,7 +866,7 @@ static void network_init(void) if (have_uuid) sysappend_set_uuid(uuid); ip_init(); - print_sysappend(); + /* print_sysappend(); */ http_bake_cookies(); /* diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 47c233f6..e07fc353 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -148,6 +148,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; -- cgit v1.2.1 From cd89ddbde2e1680f537a32010d64fd5465330122 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 9 May 2011 20:35:35 -0700 Subject: pxe: when hooking an interrupt, explicitly enable it at the PIC At least gPXE/iPXE apparently enters the NBP with the interrupt line disabled at the PIC; explicitly enable it instead. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index bbad372e..be8d38f8 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -9,6 +9,8 @@ #include "thread.h" #include "pxe.h" #include +#include +#include extern uint8_t pxe_irq_pending; static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0); @@ -18,6 +20,9 @@ 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; + uint16_t maskreg; + irq_state_t irqstate; if (irq < 8) vec = irq + 0x08; @@ -26,10 +31,20 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) else return false; + irqstate = irq_save(); + entry = (far_ptr_t *)(vec << 2); *old = *entry; entry->ptr = (uint32_t)isr; + /* Enable this interrupt at the PIC level, just in case... */ + maskreg = 0x21 + ((irq & 8) << (7-3)); + mask = inb(maskreg); + mask &= ~(1 << (irq & 3)); + outb(mask, maskreg); + + irq_restore(irqstate); + printf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec, old->seg, old->offs, entry->seg, entry->offs); -- cgit v1.2.1 From 23ffd64debdf013145da237fe34b8a12d70c228d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 9 May 2011 20:44:10 -0700 Subject: pxe: fix the cleanup routine Observe that the routine called pxe_cleanup_isr really had become the previous routine called reset_pxe(). Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 4 +++- core/fs/pxe/pxe.c | 30 +++--------------------------- core/fs/pxe/pxe.h | 2 +- 3 files changed, 7 insertions(+), 29 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index be8d38f8..a476b7d0 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -196,7 +196,7 @@ void pxe_start_isr(void) pxe_poll_thread, NULL); } -void pxe_cleanup_isr(void) +int reset_pxe(void) { static __lowmem struct s_PXENV_UNDI_CLOSE undi_close; @@ -210,4 +210,6 @@ void pxe_cleanup_isr(void) uninstall_irq_vector(pxe_undi_info.IntNumber, pxe_isr, &pxe_irq_chain); if (poll_thread) kill_thread(poll_thread); + + return undi_close.Status; } diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 867404ee..e553d8c7 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -963,9 +963,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 */ @@ -1001,30 +1001,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 @@ -1052,14 +1030,12 @@ void unload_pxe(void) uint16_t Status; /* All calls have this as the first member */ } unload_call; - pxe_cleanup_isr(); + printf("Called unload_pxe()...\n"); dprintf("FBM before unload = %d\n", BIOS_fbm); err = reset_pxe(); - dprintf("FBM after reset_pxe = %d, err = %d\n", BIOS_fbm, err); - /* If we want to keep PXE around, we still need to reset it */ if (KeepPXE || err) return; diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index e07fc353..d347f9e0 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -190,7 +190,7 @@ extern far_ptr_t pxe_irq_chain; /* isr.c */ void pxe_init_isr(void); void pxe_start_isr(void); -void pxe_cleanup_isr(void); +int reset_pxe(void); /* pxe.c */ struct url_info; -- cgit v1.2.1 From 855c51a1abaa96559e8c9dffdb922f4dad623e20 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 13 May 2011 15:00:08 -0700 Subject: pxe: be more verbose on unload failure Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 4 ++++ core/fs/pxe/pxe.c | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index a476b7d0..15287247 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -206,6 +206,10 @@ int reset_pxe(void) 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_undi_info.IntNumber) uninstall_irq_vector(pxe_undi_info.IntNumber, pxe_isr, &pxe_irq_chain); if (poll_thread) diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index e553d8c7..b743cfb9 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1011,6 +1011,12 @@ static void install_int18_hack(void) void unload_pxe(void) { /* 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 }; @@ -1048,8 +1054,8 @@ void unload_pxe(void) 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: 0x%x\n", - api, unload_call.Status); + printf("PXE unload API call %04x failed: 0x%x\n", + api, unload_call.Status); goto cant_free; } } -- cgit v1.2.1 From ef1e15adc91cd0d26fe70df00ec7d94b1417fa87 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 30 Mar 2012 14:44:03 -0700 Subject: pxe: Unmask the cascade if applicable, handle bad IRQ numbers IRQ 2 is really IRQ 9; if IRQ > 15 then it is meaningless. Also, since we enable the IRQ at the PIC "just in case", we might also have to enable the cascade for IRQ 8-15. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 15287247..0c0b8991 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -21,7 +21,6 @@ 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; - uint16_t maskreg; irq_state_t irqstate; if (irq < 8) @@ -38,10 +37,18 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) entry->ptr = (uint32_t)isr; /* Enable this interrupt at the PIC level, just in case... */ - maskreg = 0x21 + ((irq & 8) << (7-3)); - mask = inb(maskreg); - mask &= ~(1 << (irq & 3)); - outb(mask, maskreg); + if (irq >= 8) { + mask = inb(0x21); + mask &= ~(1 << 2); /* Enable cascade */ + outb(mask, 0x21); + mask = inb(0xa1); + mask &= ~(1 << (irq & 3)); + outb(0xa1, mask); + } else { + mask = inb(0x21); + mask &= ~(1 << (irq & 3)); + outb(0x21, mask); + } irq_restore(irqstate); @@ -185,13 +192,19 @@ void pxe_init_isr(void) */ void pxe_start_isr(void) { - pxe_irq_vector = pxe_undi_info.IntNumber; /* Used in 16-bit code */ + 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 (pxe_undi_info.IntNumber) - install_irq_vector(pxe_undi_info.IntNumber, pxe_isr, &pxe_irq_chain); + if (irq) + install_irq_vector(irq, pxe_isr, &pxe_irq_chain); - if (!pxe_undi_info.IntNumber || - !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) + if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, pxe_poll_thread, NULL); } @@ -210,8 +223,8 @@ int reset_pxe(void) if (undi_close.Status) printf("PXENV_UNDI_CLOSE failed: 0x%x\n", undi_close.Status); - if (pxe_undi_info.IntNumber) - uninstall_irq_vector(pxe_undi_info.IntNumber, pxe_isr, &pxe_irq_chain); + if (pxe_irq_vector) + uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain); if (poll_thread) kill_thread(poll_thread); -- cgit v1.2.1 From 9787a5c552d37b26550de38578c338fa92232545 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 30 Mar 2012 16:05:38 -0700 Subject: pxeisr: Use nonspecific EOI; add pxe_poll routine; CLD Use nonspecific EOI to signal the PIC, as that seems to be what other BIOS software does. Add a pxe_poll routine which can be used rather than doing the equivalent from protected mode. Run CLD on entry to pxe_isr. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.h | 1 + 1 file changed, 1 insertion(+) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index d347f9e0..26cec628 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -186,6 +186,7 @@ extern const uint8_t TimeoutTable[]; 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); -- cgit v1.2.1 From 030b9a942871e73e36930154e4d484657ac8e5fa Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 30 Mar 2012 17:27:04 -0700 Subject: pxe: Fix interrupt unmasking Fix the interrupt unmasking operation; order of arguments was reversed and the masking constant was incorrect. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 0c0b8991..709d48e6 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -20,7 +20,7 @@ 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; + uint8_t mask, mymask; irq_state_t irqstate; if (irq < 8) @@ -37,17 +37,18 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) 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 &= ~(1 << (irq & 3)); - outb(0xa1, mask); + mask &= mymask; + outb(mask, 0xa1); } else { mask = inb(0x21); - mask &= ~(1 << (irq & 3)); - outb(0x21, mask); + mask &= mymask; + outb(mask, 0x21); } irq_restore(irqstate); -- cgit v1.2.1 From 73caae75a315c3de65ac25c9d5f521c1a4d3847f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 30 Mar 2012 17:38:33 -0700 Subject: pxe: Make the ISR poll routine a bit saner It's not great, but it's all we have... Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 709d48e6..c073e032 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -164,9 +164,12 @@ static void pxe_poll_thread(void *dummy) (void)dummy; for (;;) { - thread_yield(); - if (pxe_isr_poll()) + cli(); + if (pxe_receive_thread_sem.count < 0 && pxe_isr_poll()) sem_up(&pxe_receive_thread_sem); + __schedule(); + sti(); + cpu_relax(); } } -- cgit v1.2.1 From 0dc83abe6a1f4ada9ecf15096539931e842b49f7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 26 Apr 2012 15:36:41 -0700 Subject: pxe, tftp: Unify UDP send and use pbuf memory The spec says memory using netbuf_ref() isn't allowed to change, so use pbuf memory. This seems to avoid an assert in the lwip stack. Signed-off-by: H. Peter Anvin --- core/fs/pxe/tftp.c | 57 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 20 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index f1e22435..9897e803 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -44,6 +44,39 @@ static void tftp_close_file(struct inode *inode) } } +/* + * Send a UDP packet. + */ +static void udp_send(struct netconn *conn, const void *data, size_t len) +{ + struct netbuf *nbuf; + void *pbuf; + int err; + + nbuf = netbuf_new(); + if (!nbuf) { + printf("netbuf allocation error\n"); + return; + } + + pbuf = netbuf_alloc(nbuf, len); + if (!pbuf) { + printf("pbuf allocation error\n"); + goto out; + } + + memcpy(pbuf, data, len); + + err = netconn_send(conn, nbuf); + if (err) { + printf("netconn_send error %d\n", err); + goto out; + } + +out: + netbuf_delete(nbuf); +} + /** * Send an ERROR packet. This is used to terminate a connection. * @@ -59,7 +92,6 @@ static void tftp_error(struct inode *inode, uint16_t errnum, uint16_t err_num; char err_msg[64]; } __packed err_buf; - struct netbuf *nbuf; int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1); struct pxe_pvt_inode *socket = PVT(inode); @@ -68,11 +100,7 @@ static void tftp_error(struct inode *inode, uint16_t errnum, memcpy(err_buf.err_msg, errstr, len); err_buf.err_msg[len] = '\0'; - nbuf = netbuf_new(); - netbuf_ref(nbuf, &err_buf, 4 + len + 1); - netconn_send(socket->conn, nbuf); - /* If something goes wrong, there is nothing we can do, anyway... */ - netbuf_delete(nbuf); + udp_send(socket->conn, &err_buf, 4 + len + 1); } /** @@ -84,23 +112,14 @@ static void tftp_error(struct inode *inode, uint16_t errnum, */ static void ack_packet(struct inode *inode, uint16_t ack_num) { - err_t err; static uint16_t ack_packet_buf[2]; - struct netbuf *nbuf; struct pxe_pvt_inode *socket = PVT(inode); /* Packet number to ack */ ack_packet_buf[0] = TFTP_ACK; ack_packet_buf[1] = htons(ack_num); - nbuf = netbuf_new(); - netbuf_ref(nbuf, ack_packet_buf, 4); - err = netconn_send(socket->conn, nbuf); - netbuf_delete(nbuf); - (void)err; -#if 0 - printf("sent %s\n", err ? "FAILED" : "OK"); -#endif + udp_send(socket->conn, ack_packet_buf, 4); } /* @@ -291,11 +310,9 @@ sendreq: return; /* No file available... */ oldtime = jiffies(); - nbuf = netbuf_new(); - netbuf_ref(nbuf, rrq_packet_buf, rrq_len); addr.addr = url->ip; - netconn_sendto(socket->conn, nbuf, &addr, url->port); - netbuf_delete(nbuf); + netconn_connect(socket->conn, &addr, url->port); + udp_send(socket->conn, rrq_packet_buf, rrq_len); /* If the WRITE call fails, we let the timeout take care of it... */ wait_pkt: -- cgit v1.2.1 From eba2e602f0a8627a3420f73eed96be6aafd422e6 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 26 Apr 2012 15:46:03 -0700 Subject: pxe, tftp: make TimeoutTable static TimeoutTable is now only used in tftp.c, so make it static. Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.h | 1 - core/fs/pxe/tftp.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 26cec628..530bc083 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -176,7 +176,6 @@ extern uint8_t uuid_type; extern uint8_t uuid[]; extern uint16_t BIOS_fbm; -extern const uint8_t TimeoutTable[]; /* * functions diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 9897e803..2710787d 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -4,7 +4,7 @@ #include "url.h" #include "tftp.h" -const uint8_t TimeoutTable[] = { +static 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 }; -- cgit v1.2.1 From 81d9a7d9eded5ac6f221020a1838d70107cb53e6 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 26 Apr 2012 15:49:52 -0700 Subject: pxe, tftp: Handle block number wraparound Variables which hold TFTP block numbers must be uint16_t, or they will not wrap around properly. Signed-off-by: H. Peter Anvin --- core/fs/pxe/tftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index 2710787d..429e8ffe 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -128,7 +128,7 @@ static void ack_packet(struct inode *inode, uint16_t ack_num) */ static void tftp_get_packet(struct inode *inode) { - int last_pkt; + uint16_t last_pkt; const uint8_t *timeout_ptr; uint8_t timeout; uint16_t buffersize; -- cgit v1.2.1 From a70b4e8f705752388f1b13edaca19a0ee5843515 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 26 Apr 2012 15:50:53 -0700 Subject: pxe: Remove stray debugging message Unloading PXE was a debugging message, use dprintf(). Signed-off-by: H. Peter Anvin --- core/fs/pxe/pxe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index b743cfb9..89a7b584 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -1036,8 +1036,7 @@ void unload_pxe(void) uint16_t Status; /* All calls have this as the first member */ } unload_call; - printf("Called unload_pxe()...\n"); - + dprintf("Called unload_pxe()...\n"); dprintf("FBM before unload = %d\n", BIOS_fbm); err = reset_pxe(); -- cgit v1.2.1 From c135aeebe08d857548a3a56b6fc3a0125ec8972c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 26 Apr 2012 15:57:51 -0700 Subject: pxe, ftp: Change anonymous password to syslinux@ Change the anonymous password from "pxelinux@" to "syslinux@". Signed-off-by: H. Peter Anvin --- core/fs/pxe/ftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 097536d1..50c92577 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -226,7 +226,7 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, if (!url->user) url->user = "anonymous"; if (!url->passwd) - url->passwd = "pxelinux@"; + url->passwd = "syslinux@"; resp = ftp_cmd_response(socket->ctl, "USER", url->user, NULL, NULL); if (resp != 202 && resp != 230) { -- cgit v1.2.1 From b2d6e6e37706f7894d876927cd786362d6162368 Mon Sep 17 00:00:00 2001 From: Gene Cumm Date: Fri, 27 Apr 2012 22:32:02 -0400 Subject: core/pxe: lwip changes to netconn_recv Signed-off-by: Gene Cumm --- core/fs/pxe/tcp.c | 4 ++-- core/fs/pxe/tftp.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c index daff4d70..ec7679e7 100644 --- a/core/fs/pxe/tcp.c +++ b/core/fs/pxe/tcp.c @@ -51,8 +51,8 @@ void tcp_fill_buffer(struct inode *inode) } /* If needed get a new netbuf */ if (!socket->buf) { - socket->buf = netconn_recv(socket->conn); - if (!socket->buf) { + err = netconn_recv(socket->conn, &(socket->buf)); + if (!socket->buf || err) { socket->tftp_goteof = 1; if (inode->size == -1) inode->size = socket->tftp_filepos; diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index f1e22435..58e8189f 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -119,6 +119,7 @@ static void tftp_get_packet(struct inode *inode) struct netbuf *nbuf; u16_t nbuf_len; struct pxe_pvt_inode *socket = PVT(inode); + err_t err; /* * Start by ACKing the previous packet; this should cause @@ -132,8 +133,8 @@ static void tftp_get_packet(struct inode *inode) ack_packet(inode, socket->tftp_lastpkt); while (timeout) { - nbuf = netconn_recv(socket->conn); - if (!nbuf) { + err = netconn_recv(socket->conn, &nbuf); + if (!nbuf || err) { jiffies_t now = jiffies(); if (now-oldtime >= timeout) { @@ -301,8 +302,8 @@ sendreq: wait_pkt: netconn_disconnect(socket->conn); for (;;) { - nbuf = netconn_recv(socket->conn); - if (!nbuf) { + err = netconn_recv(socket->conn, &nbuf); + if (!nbuf || err) { jiffies_t now = jiffies(); if (now - oldtime >= timeout) goto sendreq; -- cgit v1.2.1 From 624432a3429aa6c57eb8d2b03faf64c4edfdf0cc Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 29 May 2012 16:06:23 -0700 Subject: pxe: Add code to detect a struck interrupt line and disable (go to poll) Add code to detect a stuck interrupt line and disable it; at that point we will go to polling mode. Currently we only give ourselves 3 timer ticks after initialization to detect this, but this is good enough to deal with gPXE on virtio. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index c073e032..e2bf2f67 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -13,15 +13,19 @@ #include extern uint8_t pxe_irq_pending; +extern volatile uint8_t pxe_irq_timeout; static DECLARE_INIT_SEMAPHORE(pxe_receive_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; - irq_state_t irqstate; + uint32_t now; if (irq < 8) vec = irq + 0x08; @@ -30,7 +34,7 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) else return false; - irqstate = irq_save(); + cli(); entry = (far_ptr_t *)(vec << 2); *old = *entry; @@ -51,18 +55,28 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) outb(mask, 0x21); } - irq_restore(irqstate); + sti(); + + now = jiffies(); + + /* Some time to watch for stuck interrupts */ + while (jiffies() - now < 4 && !pxe_irq_timeout) + hlt(); + + if (pxe_irq_timeout) + *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 true; + return !pxe_irq_timeout; } 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 */ @@ -74,13 +88,19 @@ static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) else return false; + cli(); + entry = (far_ptr_t *)(vec << 2); - if (entry->ptr != (uint32_t)isr) - return false; + if (entry->ptr != (uint32_t)isr) { + rv = false; + } else { + *entry = *old; + rv = true; + } - *entry = *old; - return true; + sti(); + return rv; } static void pxe_poll_wakeups(void) @@ -205,8 +225,10 @@ void pxe_start_isr(void) pxe_irq_vector = irq; - if (irq) - install_irq_vector(irq, pxe_isr, &pxe_irq_chain); + if (irq) { + if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain)) + irq = 0; /* Install failed or stuck interrupt */ + } if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, -- cgit v1.2.1 From 7699e996c613e51049e0bad6ade32215ac263289 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 29 May 2012 19:33:20 -0700 Subject: pxe: always start the idle thread, in case we need it Always start the idle thread, but leave it blocked unless we have an indication that interrupts are not working. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index e2bf2f67..69cfe9f1 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -13,8 +13,9 @@ #include extern uint8_t pxe_irq_pending; -extern volatile uint8_t pxe_irq_timeout; +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; /* @@ -26,6 +27,7 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) unsigned int vec; uint8_t mask, mymask; uint32_t now; + bool ok; if (irq < 8) vec = irq + 0x08; @@ -36,6 +38,11 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) cli(); + if (pxe_need_poll) { + sti(); + return false; + } + entry = (far_ptr_t *)(vec << 2); *old = *entry; entry->ptr = (uint32_t)isr; @@ -60,16 +67,16 @@ static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old) now = jiffies(); /* Some time to watch for stuck interrupts */ - while (jiffies() - now < 4 && !pxe_irq_timeout) + while (jiffies() - now < 4 && (ok = !pxe_need_poll)) hlt(); - if (pxe_irq_timeout) + 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 !pxe_irq_timeout; + return ok; } static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old) @@ -108,6 +115,12 @@ 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(); @@ -183,11 +196,15 @@ 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); - __schedule(); + else + __schedule(); sti(); cpu_relax(); } @@ -206,9 +223,10 @@ void pxe_init_isr(void) * 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); - core_pm_hook = __schedule; } /* @@ -230,9 +248,11 @@ void pxe_start_isr(void) 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)) - poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY, - pxe_poll_thread, NULL); + pxe_need_poll |= 1; } int reset_pxe(void) -- cgit v1.2.1 From 79312306de0150ef64213ef9fbc5aa8580544f03 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 29 May 2012 21:08:20 -0700 Subject: pxe: use core for atomic modification of certain volatiles pxe_need_poll and pxe_irq_count can be accessed from an interrupt handler, so make sure that we modify them in a way that is locally atomic. Signed-off-by: H. Peter Anvin --- core/fs/pxe/isr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/fs') diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c index 69cfe9f1..069fefd5 100644 --- a/core/fs/pxe/isr.c +++ b/core/fs/pxe/isr.c @@ -252,7 +252,7 @@ void pxe_start_isr(void) pxe_poll_thread, NULL); if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ)) - pxe_need_poll |= 1; + asm volatile("orb $1,%0" : "+m" (pxe_need_poll)); } int reset_pxe(void) -- cgit v1.2.1 From c87df9b16191023498c2780e47527cfee3ddf015 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 18 Feb 2013 20:25:27 +0000 Subject: pxe: Handle closing file with NULL ->inode It's possible that pxe_close_file() will be called with a NULL file->inode from searchdir() if pxe_searchdir() fails to lookup a file. Signed-off-by: Matt Fleming --- core/fs/pxe/pxe.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'core/fs') diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 52787514..ff8a0b33 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -74,6 +74,9 @@ 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) { socket->ops->close(inode); } -- cgit v1.2.1 From 16aa878d78086e9bc1c1f1053dc24da295f81e05 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 26 Feb 2013 16:24:56 +0000 Subject: net: Return of the legacy network stack While it's nice having the shiny new lwIP stack, there is definitely merit in being able to choose between two different network stacks. We want to keep the legacy network stack around as it is known to handle funky BIOS implementations and provides a good reference point when bugs are suspected in the lwIP code. Users now have a choice of .0 files. pxelinux.0 uses the legacy network stack, while lpxelinux.0 uses lwIP. Note that not every protocol is converted to using this new API. The http, ftp and tcp code is still inherently tied to the netconn API, and is only available with lpxelinux.0 and the lwIP stack. It's unlikely that this code will ever be fixed up to work with the legacy network stack. Network stack operations are abstracted behind the net_core_* interface, and each network stack has private data fields contained within a struct net_private. Cc: H. Peter Anvin Cc: Eric W. Biederman Cc: Gene Cumm Signed-off-by: Matt Fleming --- core/fs/pxe/core.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++ core/fs/pxe/ftp.c | 32 ++++----- core/fs/pxe/http.c | 8 +-- core/fs/pxe/pxe.c | 36 ++-------- core/fs/pxe/pxe.h | 44 +++++++++--- core/fs/pxe/tcp.c | 28 ++++---- core/fs/pxe/tftp.c | 128 ++++++++++----------------------- 7 files changed, 316 insertions(+), 167 deletions(-) create mode 100644 core/fs/pxe/core.c (limited to 'core/fs') 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 +#include +#include +#include +#include +#include +#include "pxe.h" + +/** + * Open a socket + * + * @param:socket, the socket to open + * @param:proto, the protocol of the new connection + * + * @out: error code, 0 on success, -1 on failure + */ +int net_core_open(struct pxe_pvt_inode *socket, enum net_core_proto proto) +{ + struct net_private *priv = &socket->private; + enum netconn_type type; + int err; + + switch (proto) { + case NET_CORE_TCP: + type = NETCONN_TCP; + break; + case NET_CORE_UDP: + type = NETCONN_UDP; + break; + default: + type = NETCONN_INVALID; + break; + } + + priv->conn = netconn_new(type); + if (!priv->conn) + return -1; + + priv->conn->recv_timeout = 15; /* A 15 ms recv timeout... */ + err = netconn_bind(priv->conn, NULL, 0); + if (err) { + printf("netconn_bind error %d\n", err); + return -1; + } + + return 0; +} + +/** + * Close a socket + * + * @param:socket, the socket to open + */ +void net_core_close(struct pxe_pvt_inode *socket) +{ + struct net_private *priv = &socket->private; + + if (priv->conn) { + netconn_delete(priv->conn); + priv->conn = NULL; + } +} + +/** + * Establish a connection on an open socket + * + * @param:socket, the open socket + * @param:ip, the ip address + * @param:port, the port number, host-byte order + */ +void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip, + uint16_t port) +{ + struct net_private *priv = &socket->private; + struct ip_addr addr; + + addr.addr = ip; + netconn_connect(priv->conn, &addr, port); +} + +/** + * Tear down a connection on an open socket + * + * @param:socket, the open socket + */ +void net_core_disconnect(struct pxe_pvt_inode *socket) +{ + struct net_private *priv = &socket->private; + netconn_disconnect(priv->conn); +} + +/** + * Read data from the network stack + * + * @param:socket, the open socket + * @param:buf, location of buffer to store data + * @param:buf_len, size of buffer + + * @out: src_ip, ip address of the data source + * @out: src_port, port number of the data source, host-byte order + */ +int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len, + uint32_t *src_ip, uint16_t *src_port) +{ + struct net_private *priv = &socket->private; + struct netbuf *nbuf; + u16_t nbuf_len; + int err; + + err = netconn_recv(priv->conn, &nbuf); + if (err) + return err; + + if (!nbuf) + return -1; + + *src_ip = netbuf_fromaddr(nbuf)->addr; + *src_port = netbuf_fromport(nbuf); + + netbuf_first(nbuf); /* XXX needed? */ + nbuf_len = netbuf_len(nbuf); + if (nbuf_len <= *buf_len) + netbuf_copy(nbuf, buf, nbuf_len); + else + nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ + netbuf_delete(nbuf); + + *buf_len = nbuf_len; + return 0; +} + +/** + * Send a UDP packet. + * + * @param:socket, the open socket + * @param:data, data buffer to send + * @param:len, size of data bufer + */ +void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len) +{ + struct netconn *conn = socket->private.conn; + struct netbuf *nbuf; + void *pbuf; + int err; + + nbuf = netbuf_new(); + if (!nbuf) { + printf("netbuf allocation error\n"); + return; + } + + pbuf = netbuf_alloc(nbuf, len); + if (!pbuf) { + printf("pbuf allocation error\n"); + goto out; + } + + memcpy(pbuf, data, len); + + err = netconn_send(conn, nbuf); + if (err) { + printf("netconn_send error %d\n", err); + goto out; + } + +out: + netbuf_delete(nbuf); +} + +/** + * Network stack-specific initialization + */ +void net_core_init(void) +{ + int err; + int i; + + http_bake_cookies(); + + /* Initialize lwip */ + tcpip_init(NULL, NULL); + + /* Start up the undi driver for lwip */ + err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway); + if (err) { + printf("undiif driver failed to start: %d\n", err); + kaboom(); + } + + for (i = 0; i < DNS_MAX_SERVERS; i++) { + /* Transfer the DNS information to lwip */ + dns_setserver(i, (struct ip_addr *)&dns_server[i]); + } +} + +void probe_undi(void) +{ + /* Probe UNDI information */ + pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info); + pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface); + + printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n", + pxe_undi_info.BaseIo, pxe_undi_info.IntNumber, + pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType, + pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags); +} + diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c index 50c92577..5d5a6f03 100644 --- a/core/fs/pxe/ftp.c +++ b/core/fs/pxe/ftp.c @@ -69,7 +69,7 @@ static int ftp_cmd_response(struct inode *inode, const char *cmd, *q++ = '\n'; cmd_len += 2; - err = netconn_write(socket->conn, cmd_buf, cmd_len, NETCONN_COPY); + err = netconn_write(socket->private.conn, cmd_buf, cmd_len, NETCONN_COPY); if (err) return -1; } @@ -166,7 +166,7 @@ static void ftp_close_file(struct inode *inode) int resp; ctlsock = socket->ctl ? PVT(socket->ctl) : NULL; - if (ctlsock->conn) { + if (ctlsock->private.conn) { resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL); while (resp == 226) { resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL); @@ -209,11 +209,11 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, return; ctlsock = PVT(socket->ctl); ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */ - ctlsock->conn = netconn_new(NETCONN_TCP); - if (!ctlsock->conn) + ctlsock->private.conn = netconn_new(NETCONN_TCP); + if (!ctlsock->private.conn) goto err_free; addr.addr = url->ip; - err = netconn_connect(ctlsock->conn, &addr, url->port); + err = netconn_connect(ctlsock->private.conn, &addr, url->port); if (err) goto err_delete; @@ -248,10 +248,10 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, if (resp != 227 || pasv_bytes != 6) goto err_disconnect; - socket->conn = netconn_new(NETCONN_TCP); - if (!socket->conn) + socket->private.conn = netconn_new(NETCONN_TCP); + if (!socket->private.conn) goto err_disconnect; - err = netconn_connect(socket->conn, (struct ip_addr *)&pasv_data[0], + err = netconn_connect(socket->private.conn, (struct ip_addr *)&pasv_data[0], ntohs(*(uint16_t *)&pasv_data[4])); if (err) goto err_disconnect; @@ -266,15 +266,15 @@ void ftp_open(struct url_info *url, int flags, struct inode *inode, return; /* Sucess! */ err_disconnect: - if (ctlsock->conn) - netconn_write(ctlsock->conn, "QUIT\r\n", 6, NETCONN_NOCOPY); - if (socket->conn) - netconn_delete(socket->conn); - if (ctlsock->buf) - netbuf_delete(ctlsock->buf); + if (ctlsock->private.conn) + netconn_write(ctlsock->private.conn, "QUIT\r\n", 6, NETCONN_NOCOPY); + if (socket->private.conn) + netconn_delete(socket->private.conn); + if (ctlsock->private.buf) + netbuf_delete(ctlsock->private.buf); err_delete: - if (ctlsock->conn) - netconn_delete(ctlsock->conn); + if (ctlsock->private.conn) + netconn_delete(ctlsock->private.conn); err_free: free_socket(socket->ctl); } diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index d5022eb2..9bf0c92e 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -191,8 +191,8 @@ void http_open(struct url_info *url, int flags, struct inode *inode, inode->size = content_length = -1; /* Start the http connection */ - socket->conn = netconn_new(NETCONN_TCP); - if (!socket->conn) { + socket->private.conn = netconn_new(NETCONN_TCP); + if (!socket->private.conn) { printf("netconn_new failed\n"); return; } @@ -201,7 +201,7 @@ void http_open(struct url_info *url, int flags, struct inode *inode, if (!url->port) url->port = HTTP_PORT; - err = netconn_connect(socket->conn, &addr, url->port); + err = netconn_connect(socket->private.conn, &addr, url->port); if (err) { printf("netconn_connect error %d\n", err); goto fail; @@ -225,7 +225,7 @@ void http_open(struct url_info *url, int flags, struct inode *inode, if (header_bytes >= header_len) goto fail; /* Buffer overflow */ - err = netconn_write(socket->conn, header_buf, + err = netconn_write(socket->private.conn, header_buf, header_bytes, NETCONN_NOCOPY); if (err) { printf("netconn_write error %d\n", err); diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index ff8a0b33..9f18f282 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -7,14 +7,11 @@ #include #include #include -#include -#include -#include -#include #include "pxe.h" #include "thread.h" #include "url.h" #include "tftp.h" +#include __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info; __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface; @@ -38,8 +35,10 @@ static struct url_scheme { int ok_flags; } url_schemes[] = { { "tftp", tftp_open, 0 }, +#ifdef IS_LPXELINUX { "http", http_open, O_DIRECTORY }, { "ftp", ftp_open, O_DIRECTORY }, +#endif { NULL, NULL, 0 }, }; #define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY) @@ -772,14 +771,7 @@ static int pxe_init(bool quiet) real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */ - /* Probe UNDI information */ - pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info); - pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface); - - printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n", - pxe_undi_info.BaseIo, pxe_undi_info.IntNumber, - pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType, - pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags); + probe_undi(); return 0; } @@ -809,9 +801,7 @@ static void gpxe_init(void) */ static void network_init(void) { - int err; int pkt_len; - int i; struct bootp_t *bp; const size_t dhcp_max_packet = 4096; @@ -870,9 +860,8 @@ static void network_init(void) if (have_uuid) sysappend_set_uuid(uuid); ip_init(); - /* print_sysappend(); */ - http_bake_cookies(); + /* print_sysappend(); */ /* * Check to see if we got any PXELINUX-specific DHCP options; in particular, * if we didn't get the magic enable, do not recognize any other options. @@ -880,20 +869,7 @@ static void network_init(void) if ((DHCPMagic & 1) == 0) DHCPMagic = 0; - /* Initialize lwip */ - tcpip_init(NULL, NULL); - - /* Start up the undi driver for lwip */ - err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway); - if (err) { - printf("undiif driver failed to start: %d\n", err); - kaboom(); - } - - for (i = 0; i < DNS_MAX_SERVERS; i++) { - /* Transfer the DNS information to lwip */ - dns_setserver(i, (struct ip_addr *)&dns_server[i]); - } + net_core_init(); } /* diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 4a43a9db..2d4be3b1 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -114,19 +114,29 @@ struct pxe_conn_ops { int (*readdir)(struct inode *inode, struct dirent *dirent); }; -struct pxe_pvt_inode { +struct net_private { +#ifdef IS_LPXELINUX struct netconn *conn; /* lwip network connection */ struct netbuf *buf; /* lwip cached buffer */ - uint16_t tftp_remoteport; /* Remote port number */ - uint32_t tftp_filepos; /* bytes downloaded (including buffer) */ - uint32_t tftp_blksize; /* Block size for this connection(*) */ - uint16_t tftp_bytesleft; /* Unclaimed data bytes */ - uint16_t tftp_lastpkt; /* Sequence number of last packet (HBO) */ - char *tftp_dataptr; /* Pointer to available data */ - uint8_t tftp_goteof; /* 1 if the EOF packet received */ - uint8_t tftp_unused[3]; /* Currently unused */ - char *tftp_pktbuf; /* Packet buffer */ - struct inode *ctl; /* Control connection (for FTP) */ +#else + uint16_t tftp_localport; /* Local port number (0=not in use) */ + uint32_t tftp_remoteip; /* Remote IP address (0 = disconnected) */ +#endif + +}; + +struct pxe_pvt_inode { + struct net_private private; /* Network stack private data */ + uint16_t tftp_remoteport; /* Remote port number */ + uint32_t tftp_filepos; /* bytes downloaded (including buffer) */ + uint32_t tftp_blksize; /* Block size for this connection(*) */ + uint16_t tftp_bytesleft; /* Unclaimed data bytes */ + uint16_t tftp_lastpkt; /* Sequence number of last packet (HBO) */ + char *tftp_dataptr; /* Pointer to available data */ + uint8_t tftp_goteof; /* 1 if the EOF packet received */ + uint8_t tftp_unused[3]; /* Currently unused */ + char *tftp_pktbuf; /* Packet buffer */ + struct inode *ctl; /* Control connection (for FTP) */ const struct pxe_conn_ops *ops; }; @@ -175,6 +185,18 @@ extern bool have_uuid; extern uint8_t uuid_type; extern uint8_t uuid[]; +/* + * Compute the suitable gateway for a specific route -- too many + * vendor PXE stacks don't do this correctly... + */ +static inline uint32_t gateway(uint32_t ip) +{ + if ((ip ^ IPInfo.myip) & IPInfo.netmask) + return IPInfo.gateway; + else + return 0; +} + /* * functions */ diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c index ec7679e7..528fbce8 100644 --- a/core/fs/pxe/tcp.c +++ b/core/fs/pxe/tcp.c @@ -25,13 +25,13 @@ void tcp_close_file(struct inode *inode) { struct pxe_pvt_inode *socket = PVT(inode); - if (socket->conn) { - netconn_delete(socket->conn); - socket->conn = NULL; + if (socket->private.conn) { + netconn_delete(socket->private.conn); + socket->private.conn = NULL; } - if (socket->buf) { - netbuf_delete(socket->buf); - socket->buf = NULL; + if (socket->private.buf) { + netbuf_delete(socket->private.buf); + socket->private.buf = NULL; } } @@ -43,16 +43,16 @@ void tcp_fill_buffer(struct inode *inode) err_t err; /* Clean up or advance an inuse netbuf */ - if (socket->buf) { - if (netbuf_next(socket->buf) < 0) { - netbuf_delete(socket->buf); - socket->buf = NULL; + if (socket->private.buf) { + if (netbuf_next(socket->private.buf) < 0) { + netbuf_delete(socket->private.buf); + socket->private.buf = NULL; } } /* If needed get a new netbuf */ - if (!socket->buf) { - err = netconn_recv(socket->conn, &(socket->buf)); - if (!socket->buf || err) { + if (!socket->private.buf) { + err = netconn_recv(socket->private.conn, &(socket->private.buf)); + if (!socket->private.buf || err) { socket->tftp_goteof = 1; if (inode->size == -1) inode->size = socket->tftp_filepos; @@ -61,7 +61,7 @@ void tcp_fill_buffer(struct inode *inode) } } /* Report the current fragment of the netbuf */ - err = netbuf_data(socket->buf, &data, &len); + err = netbuf_data(socket->private.buf, &data, &len); if (err) { printf("netbuf_data err: %d\n", err); kaboom(); diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c index f6ea2974..9b755c93 100644 --- a/core/fs/pxe/tftp.c +++ b/core/fs/pxe/tftp.c @@ -1,10 +1,10 @@ #include -#include +#include #include "pxe.h" #include "url.h" #include "tftp.h" -static const uint8_t TimeoutTable[] = { +const uint8_t TimeoutTable[] = { 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0 }; @@ -38,43 +38,7 @@ static void tftp_close_file(struct inode *inode) if (!socket->tftp_goteof) { tftp_error(inode, 0, "No error, file close"); } - if (socket->conn) { - netconn_delete(socket->conn); - socket->conn = NULL; - } -} - -/* - * Send a UDP packet. - */ -static void udp_send(struct netconn *conn, const void *data, size_t len) -{ - struct netbuf *nbuf; - void *pbuf; - int err; - - nbuf = netbuf_new(); - if (!nbuf) { - printf("netbuf allocation error\n"); - return; - } - - pbuf = netbuf_alloc(nbuf, len); - if (!pbuf) { - printf("pbuf allocation error\n"); - goto out; - } - - memcpy(pbuf, data, len); - - err = netconn_send(conn, nbuf); - if (err) { - printf("netconn_send error %d\n", err); - goto out; - } - -out: - netbuf_delete(nbuf); + net_core_close(socket); } /** @@ -100,7 +64,7 @@ static void tftp_error(struct inode *inode, uint16_t errnum, memcpy(err_buf.err_msg, errstr, len); err_buf.err_msg[len] = '\0'; - udp_send(socket->conn, &err_buf, 4 + len + 1); + net_core_send(socket, &err_buf, 4 + len + 1); } /** @@ -119,7 +83,7 @@ static void ack_packet(struct inode *inode, uint16_t ack_num) ack_packet_buf[0] = TFTP_ACK; ack_packet_buf[1] = htons(ack_num); - udp_send(socket->conn, ack_packet_buf, 4); + net_core_send(socket, ack_packet_buf, 4); } /* @@ -135,10 +99,11 @@ static void tftp_get_packet(struct inode *inode) uint16_t serial; jiffies_t oldtime; struct tftp_packet *pkt = NULL; - struct netbuf *nbuf; - u16_t nbuf_len; + uint16_t buf_len; struct pxe_pvt_inode *socket = PVT(inode); - err_t err; + uint16_t src_port; + uint32_t src_ip; + int err; /* * Start by ACKing the previous packet; this should cause @@ -152,8 +117,10 @@ static void tftp_get_packet(struct inode *inode) ack_packet(inode, socket->tftp_lastpkt); while (timeout) { - err = netconn_recv(socket->conn, &nbuf); - if (!nbuf || err) { + buf_len = socket->tftp_blksize + 4; + err = net_core_recv(socket, socket->tftp_pktbuf, &buf_len, + &src_ip, &src_port); + if (err) { jiffies_t now = jiffies(); if (now-oldtime >= timeout) { @@ -166,15 +133,7 @@ static void tftp_get_packet(struct inode *inode) continue; } - netbuf_first(nbuf); - nbuf_len = 0; - nbuf_len = netbuf_len(nbuf); - if (nbuf_len <= socket->tftp_blksize + 4) - netbuf_copy(nbuf, socket->tftp_pktbuf, nbuf_len); - else - nbuf_len = 0; /* invalid packet */ - netbuf_delete(nbuf); - if (nbuf_len < 4) /* Bad size for a DATA packet */ + if (buf_len < 4) /* Bad size for a DATA packet */ continue; pkt = (struct tftp_packet *)(socket->tftp_pktbuf); @@ -207,7 +166,7 @@ static void tftp_get_packet(struct inode *inode) /* It's the packet we want. We're also EOF if the size < blocksize */ socket->tftp_lastpkt = last_pkt; /* Update last packet number */ - buffersize = nbuf_len - 4; /* Skip TFTP header */ + buffersize = buf_len - 4; /* Skip TFTP header */ socket->tftp_dataptr = socket->tftp_pktbuf + 4; socket->tftp_filepos += buffersize; socket->tftp_bytesleft = buffersize; @@ -243,8 +202,7 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, { struct pxe_pvt_inode *socket = PVT(inode); char *buf; - struct netbuf *nbuf; - u16_t nbuf_len; + uint16_t buf_len; char *p; char *options; char *data; @@ -262,7 +220,8 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, uint16_t opcode; uint16_t blk_num; uint32_t opdata, *opdata_ptr; - struct ip_addr addr; + uint16_t src_port; + uint32_t src_ip; (void)redir; /* TFTP does not redirect */ (void)flags; @@ -279,17 +238,9 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, url->port = TFTP_PORT; socket->ops = &tftp_conn_ops; - socket->conn = netconn_new(NETCONN_UDP); - if (!socket->conn) + if (net_core_open(socket, NET_CORE_UDP)) return; - socket->conn->recv_timeout = 15; /* A 15 ms recv timeout... */ - err = netconn_bind(socket->conn, NULL, 0); - if (err) { - printf("netconn_bind error %d\n", err); - return; - } - buf = rrq_packet_buf; *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ buf += 2; @@ -303,48 +254,42 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode, rrq_len = buf - rrq_packet_buf; timeout_ptr = TimeoutTable; /* Reset timeout */ - sendreq: - netconn_disconnect(socket->conn); + net_core_disconnect(socket); timeout = *timeout_ptr++; if (!timeout) return; /* No file available... */ oldtime = jiffies(); - addr.addr = url->ip; - netconn_connect(socket->conn, &addr, url->port); - udp_send(socket->conn, rrq_packet_buf, rrq_len); + net_core_connect(socket, url->ip, url->port); + net_core_send(socket, rrq_packet_buf, rrq_len); /* If the WRITE call fails, we let the timeout take care of it... */ wait_pkt: - netconn_disconnect(socket->conn); + net_core_disconnect(socket); for (;;) { - err = netconn_recv(socket->conn, &nbuf); - if (!nbuf || err) { + buf_len = sizeof(reply_packet_buf); + + err = net_core_recv(socket, reply_packet_buf, &buf_len, + &src_ip, &src_port); + if (err) { jiffies_t now = jiffies(); if (now - oldtime >= timeout) goto sendreq; } else { /* Make sure the packet actually came from the server */ - bool ok_source; - ok_source = netbuf_fromaddr(nbuf)->addr == url->ip; - nbuf_len = netbuf_len(nbuf); - if (nbuf_len <= PKTBUF_SIZE) - netbuf_copy(nbuf, reply_packet_buf, nbuf_len); - else - nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */ - netbuf_delete(nbuf); - if (ok_source) - break; + if (src_ip == url->ip) + break; } } - netconn_connect(socket->conn, netbuf_fromaddr(nbuf), netbuf_fromport(nbuf)); + net_core_disconnect(socket); + net_core_connect(socket, src_ip, src_port); /* filesize <- -1 == unknown */ inode->size = -1; socket->tftp_blksize = TFTP_BLOCKSIZE; - buffersize = nbuf_len - 2; /* bytes after opcode */ + buffersize = buf_len - 2; /* bytes after opcode */ if (buffersize < 0) goto wait_pkt; /* Garbled reply */ @@ -491,9 +436,8 @@ err_reply: inode->size = 0; done: - if (!inode->size) { - netconn_delete(socket->conn); - socket->conn = NULL; - } + if (!inode->size) + net_core_close(socket); + return; } -- cgit v1.2.1