X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=net%2Ftftp.c;h=451d73529f2af9285e3c9ae9f9f26d2ab7dbb90f;hb=b8e09898919e23c5d7f1934be7bf9a3a6f0deb0e;hp=585eb6ef0cb3a14469894d64b436f7c90c22e23c;hpb=b62cd976543ae1b90050346c870d8357d749fe2d;p=platform%2Fkernel%2Fu-boot.git diff --git a/net/tftp.c b/net/tftp.c index 585eb6e..451d735 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -5,19 +5,18 @@ * Copyright 2011 Comelit Group SpA, * Luca Ceresoli */ - #include #include #include #include #include +#include +#include #include #include +#include #include #include "bootp.h" -#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP -#include -#endif DECLARE_GLOBAL_DATA_PTR; @@ -25,12 +24,6 @@ DECLARE_GLOBAL_DATA_PTR; #define WELL_KNOWN_PORT 69 /* Millisecs to timeout for lost pkt */ #define TIMEOUT 5000UL -#ifndef CONFIG_NET_RETRY_COUNT -/* # of timeouts before giving up */ -# define TIMEOUT_COUNT 10 -#else -# define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT * 2) -#endif /* Number of "loading" hashes per line (for checking the image size) */ #define HASHES_PER_LINE 65 @@ -45,7 +38,7 @@ DECLARE_GLOBAL_DATA_PTR; #define TFTP_OACK 6 static ulong timeout_ms = TIMEOUT; -static int timeout_count_max = TIMEOUT_COUNT; +static int timeout_count_max = (CONFIG_NET_RETRY_COUNT * 2); static ulong time_start; /* Record time we started tftp */ /* @@ -58,7 +51,7 @@ static ulong time_start; /* Record time we started tftp */ * non-standard timeout behavior when initiating a TFTP transfer. */ ulong tftp_timeout_ms = TIMEOUT; -int tftp_timeout_count_max = TIMEOUT_COUNT; +int tftp_timeout_count_max = (CONFIG_NET_RETRY_COUNT * 2); enum { TFTP_ERR_UNDEFINED = 0, @@ -68,6 +61,7 @@ enum { TFTP_ERR_UNEXPECTED_OPCODE = 4, TFTP_ERR_UNKNOWN_TRANSFER_ID = 5, TFTP_ERR_FILE_ALREADY_EXISTS = 6, + TFTP_ERR_OPTION_NEGOTIATION = 8, }; static struct in_addr tftp_remote_ip; @@ -95,6 +89,12 @@ static int tftp_tsize; /* The number of hashes we printed */ static short tftp_tsize_num_hash; #endif +/* The window size negotiated */ +static ushort tftp_windowsize; +/* Next block to send ack to */ +static ushort tftp_next_ack; +/* Last nack block we send */ +static ushort tftp_last_nack; #ifdef CONFIG_CMD_TFTPPUT /* 1 if writing, else 0 */ static int tftp_put_active; @@ -111,6 +111,7 @@ static int tftp_put_final_block_sent; #define STATE_OACK 5 #define STATE_RECV_WRQ 6 #define STATE_SEND_WRQ 7 +#define STATE_INVALID_OPTION 8 /* default TFTP block size */ #define TFTP_BLOCK_SIZE 512 @@ -133,61 +134,45 @@ static char tftp_filename[MAX_LEN]; * almost-MTU block sizes. At least try... fall back to 512 if need be. * (but those using CONFIG_IP_DEFRAG may want to set a larger block in cfg file) */ -#ifdef CONFIG_TFTP_BLOCKSIZE -#define TFTP_MTU_BLOCKSIZE CONFIG_TFTP_BLOCKSIZE + +/* When windowsize is defined to 1, + * tftp behaves the same way as it was + * never declared + */ +#ifdef CONFIG_TFTP_WINDOWSIZE +#define TFTP_WINDOWSIZE CONFIG_TFTP_WINDOWSIZE #else -#define TFTP_MTU_BLOCKSIZE 1468 +#define TFTP_WINDOWSIZE 1 #endif static unsigned short tftp_block_size = TFTP_BLOCK_SIZE; -static unsigned short tftp_block_size_option = TFTP_MTU_BLOCKSIZE; +static unsigned short tftp_block_size_option = CONFIG_TFTP_BLOCKSIZE; +static unsigned short tftp_window_size_option = TFTP_WINDOWSIZE; static inline int store_block(int block, uchar *src, unsigned int len) { - ulong offset = block * tftp_block_size + tftp_block_wrap_offset; + ulong offset = block * tftp_block_size + tftp_block_wrap_offset - + tftp_block_size; ulong newsize = offset + len; ulong store_addr = tftp_load_addr + offset; -#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP - int i, rc = 0; - - for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { - /* start address in flash? */ - if (flash_info[i].flash_id == FLASH_UNKNOWN) - continue; - if (store_addr >= flash_info[i].start[0]) { - rc = 1; - break; - } - } - - if (rc) { /* Flash is destination for this packet */ - rc = flash_write((char *)src, store_addr, len); - if (rc) { - flash_perror(rc); - return rc; - } - } else -#endif /* CONFIG_SYS_DIRECT_FLASH_TFTP */ - { - void *ptr; + void *ptr; #ifdef CONFIG_LMB - ulong end_addr = tftp_load_addr + tftp_load_size; + ulong end_addr = tftp_load_addr + tftp_load_size; - if (!end_addr) - end_addr = ULONG_MAX; + if (!end_addr) + end_addr = ULONG_MAX; - if (store_addr < tftp_load_addr || - store_addr + len > end_addr) { - puts("\nTFTP error: "); - puts("trying to overwrite reserved memory...\n"); - return -1; - } -#endif - ptr = map_sysmem(store_addr, len); - memcpy(ptr, src, len); - unmap_sysmem(ptr); + if (store_addr < tftp_load_addr || + store_addr + len > end_addr) { + puts("\nTFTP error: "); + puts("trying to overwrite reserved memory...\n"); + return -1; } +#endif + ptr = map_sysmem(store_addr, len); + memcpy(ptr, src, len); + unmap_sysmem(ptr); if (net_boot_file_size < newsize) net_boot_file_size = newsize; @@ -213,12 +198,13 @@ static void new_transfer(void) * @param block Block number to send * @param dst Destination buffer for data * @param len Number of bytes in block (this one and every other) - * @return number of bytes loaded + * Return: number of bytes loaded */ static int load_block(unsigned block, uchar *dst, unsigned len) { /* We may want to get the final block from the previous set */ - ulong offset = ((int)block - 1) * len + tftp_block_wrap_offset; + ulong offset = block * tftp_block_size + tftp_block_wrap_offset - + tftp_block_size; ulong tosend = len; tosend = min(net_boot_file_size - offset, tosend); @@ -236,9 +222,11 @@ static void tftp_timeout_handler(void); static void show_block_marker(void) { + ulong pos; + #ifdef CONFIG_TFTP_TSIZE if (tftp_tsize) { - ulong pos = tftp_cur_block * tftp_block_size + + pos = tftp_cur_block * tftp_block_size + tftp_block_wrap_offset; if (pos > tftp_tsize) pos = tftp_tsize; @@ -250,9 +238,11 @@ static void show_block_marker(void) } else #endif { - if (((tftp_cur_block - 1) % 10) == 0) + pos = (tftp_cur_block - 1) + + (tftp_block_wrap * TFTP_SEQUENCE_SIZE); + if ((pos % 10) == 0) putc('#'); - else if ((tftp_cur_block % (10 * HASHES_PER_LINE)) == 0) + else if (((pos + 1) % (10 * HASHES_PER_LINE)) == 0) puts("\n\t "); } } @@ -285,9 +275,8 @@ static void update_block_number(void) tftp_block_wrap++; tftp_block_wrap_offset += tftp_block_size * TFTP_SEQUENCE_SIZE; timeout_count = 0; /* we've done well, reset the timeout */ - } else { - show_block_marker(); } + show_block_marker(); } /* The TFTP get or put is complete */ @@ -309,6 +298,12 @@ static void tftp_complete(void) time_start * 1000, "/s"); } puts("\ndone\n"); + if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) { + if (!tftp_put_active) + efi_set_bootdev("Net", "", tftp_filename, + map_sysmem(tftp_load_addr, 0), + net_boot_file_size); + } net_set_state(NETLOOP_SUCCESS); } @@ -318,6 +313,7 @@ static void tftp_send(void) uchar *xp; int len = 0; ushort *s; + bool err_pkt = false; /* * We will always be sending some sort of packet, so @@ -353,6 +349,14 @@ static void tftp_send(void) /* try for more effic. blk size */ pkt += sprintf((char *)pkt, "blksize%c%d%c", 0, tftp_block_size_option, 0); + + /* try for more effic. window size. + * Implemented only for tftp get. + * Don't bother sending if it's 1 + */ + if (tftp_state == STATE_SEND_RRQ && tftp_window_size_option > 1) + pkt += sprintf((char *)pkt, "windowsize%c%d%c", + 0, tftp_window_size_option, 0); len = pkt - xp; break; @@ -388,6 +392,7 @@ static void tftp_send(void) strcpy((char *)pkt, "File too large"); pkt += 14 /*strlen("File too large")*/ + 1; len = pkt - xp; + err_pkt = true; break; case STATE_BAD_MAGIC: @@ -399,11 +404,28 @@ static void tftp_send(void) strcpy((char *)pkt, "File has bad magic"); pkt += 18 /*strlen("File has bad magic")*/ + 1; len = pkt - xp; + err_pkt = true; + break; + + case STATE_INVALID_OPTION: + xp = pkt; + s = (ushort *)pkt; + *s++ = htons(TFTP_ERROR); + *s++ = htons(TFTP_ERR_OPTION_NEGOTIATION); + pkt = (uchar *)s; + strcpy((char *)pkt, "Option Negotiation Failed"); + /* strlen("Option Negotiation Failed") + NULL*/ + pkt += 25 + 1; + len = pkt - xp; + err_pkt = true; break; } net_send_udp_packet(net_server_ethaddr, tftp_remote_ip, tftp_remote_port, tftp_our_port, len); + + if (err_pkt) + net_set_state(NETLOOP_FAIL); } #ifdef CONFIG_CMD_TFTPPUT @@ -424,6 +446,7 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, __be16 proto; __be16 *s; int i; + u16 timeout_val_rcvd; if (dest != tftp_our_port) { return; @@ -456,6 +479,7 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, int block = ntohs(*s); int ack_ok = (tftp_cur_block == block); + tftp_prev_block = tftp_cur_block; tftp_cur_block = (unsigned short)(block + 1); update_block_number(); if (ack_ok) @@ -480,8 +504,14 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, #endif case TFTP_OACK: - debug("Got OACK: %s %s\n", - pkt, pkt + strlen((char *)pkt) + 1); + debug("Got OACK: "); + for (i = 0; i < len; i++) { + if (pkt[i] == '\0') + debug(" "); + else + debug("%c", pkt[i]); + } + debug("\n"); tftp_state = STATE_OACK; tftp_remote_port = src; /* @@ -490,24 +520,48 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, * something like "len-8" may give a *huge* number */ for (i = 0; i+8 < len; i++) { - if (strcmp((char *)pkt + i, "blksize") == 0) { + if (strcasecmp((char *)pkt + i, "blksize") == 0) { tftp_block_size = (unsigned short) - simple_strtoul((char *)pkt + i + 8, - NULL, 10); - debug("Blocksize ack: %s, %d\n", + dectoul((char *)pkt + i + 8, NULL); + debug("Blocksize oack: %s, %d\n", (char *)pkt + i + 8, tftp_block_size); + if (tftp_block_size > tftp_block_size_option) { + printf("Invalid blk size(=%d)\n", + tftp_block_size); + tftp_state = STATE_INVALID_OPTION; + } + } + if (strcasecmp((char *)pkt + i, "timeout") == 0) { + timeout_val_rcvd = (unsigned short) + dectoul((char *)pkt + i + 8, NULL); + debug("Timeout oack: %s, %d\n", + (char *)pkt + i + 8, timeout_val_rcvd); + if (timeout_val_rcvd != (timeout_ms / 1000)) { + printf("Invalid timeout val(=%d s)\n", + timeout_val_rcvd); + tftp_state = STATE_INVALID_OPTION; + } } #ifdef CONFIG_TFTP_TSIZE - if (strcmp((char *)pkt+i, "tsize") == 0) { - tftp_tsize = simple_strtoul((char *)pkt + i + 6, - NULL, 10); + if (strcasecmp((char *)pkt + i, "tsize") == 0) { + tftp_tsize = dectoul((char *)pkt + i + 6, + NULL); debug("size = %s, %d\n", (char *)pkt + i + 6, tftp_tsize); } #endif + if (strcasecmp((char *)pkt + i, "windowsize") == 0) { + tftp_windowsize = + dectoul((char *)pkt + i + 11, NULL); + debug("windowsize = %s, %d\n", + (char *)pkt + i + 11, tftp_windowsize); + } } + + tftp_next_ack = tftp_windowsize; + #ifdef CONFIG_CMD_TFTPPUT - if (tftp_put_active) { + if (tftp_put_active && tftp_state == STATE_OACK) { /* Get ready to send the first block */ tftp_state = STATE_DATA; tftp_cur_block++; @@ -519,12 +573,33 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, if (len < 2) return; len -= 2; - tftp_cur_block = ntohs(*(__be16 *)pkt); - update_block_number(); + if (ntohs(*(__be16 *)pkt) != (ushort)(tftp_cur_block + 1)) { + debug("Received unexpected block: %d, expected: %d\n", + ntohs(*(__be16 *)pkt), + (ushort)(tftp_cur_block + 1)); + /* + * If one packet is dropped most likely + * all other buffers in the window + * that will arrive will cause a sending NACK. + * This just overwellms the server, let's just send one. + */ + if (tftp_last_nack != tftp_cur_block) { + tftp_send(); + tftp_last_nack = tftp_cur_block; + tftp_next_ack = (ushort)(tftp_cur_block + + tftp_windowsize); + } + break; + } + + tftp_cur_block++; + tftp_cur_block %= TFTP_SEQUENCE_SIZE; - if (tftp_state == STATE_SEND_RRQ) - debug("Server did not acknowledge timeout option!\n"); + if (tftp_state == STATE_SEND_RRQ) { + debug("Server did not acknowledge any options!\n"); + tftp_next_ack = tftp_windowsize; + } if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK || tftp_state == STATE_RECV_WRQ) { @@ -548,24 +623,31 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, break; } + update_block_number(); tftp_prev_block = tftp_cur_block; timeout_count_max = tftp_timeout_count_max; net_set_timeout_handler(timeout_ms, tftp_timeout_handler); - if (store_block(tftp_cur_block - 1, pkt + 2, len)) { + if (store_block(tftp_cur_block, pkt + 2, len)) { eth_halt(); net_set_state(NETLOOP_FAIL); break; } + if (len < tftp_block_size) { + tftp_send(); + tftp_complete(); + break; + } + /* * Acknowledge the block just received, which will prompt * the remote for the next one. */ - tftp_send(); - - if (len < tftp_block_size) - tftp_complete(); + if (tftp_cur_block == tftp_next_ack) { + tftp_send(); + tftp_next_ack += tftp_windowsize; + } break; case TFTP_ERROR: @@ -639,6 +721,10 @@ void tftp_start(enum proto_t protocol) if (ep != NULL) tftp_block_size_option = simple_strtol(ep, NULL, 10); + ep = env_get("tftpwindowsize"); + if (ep != NULL) + tftp_window_size_option = simple_strtol(ep, NULL, 10); + ep = env_get("tftptimeout"); if (ep != NULL) timeout_ms = simple_strtol(ep, NULL, 10); @@ -660,8 +746,8 @@ void tftp_start(enum proto_t protocol) } #endif - debug("TFTP blocksize = %i, timeout = %ld ms\n", - tftp_block_size_option, timeout_ms); + debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n", + tftp_block_size_option, tftp_window_size_option, timeout_ms); tftp_remote_ip = net_server_ip; if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) { @@ -730,9 +816,6 @@ void tftp_start(enum proto_t protocol) printf("Load address: 0x%lx\n", tftp_load_addr); puts("Loading: *\b"); tftp_state = STATE_SEND_RRQ; -#ifdef CONFIG_CMD_BOOTEFI - efi_set_bootdev("Net", "", tftp_filename); -#endif } time_start = get_timer(0); @@ -757,7 +840,8 @@ void tftp_start(enum proto_t protocol) tftp_our_port = simple_strtol(ep, NULL, 10); #endif tftp_cur_block = 0; - + tftp_windowsize = 1; + tftp_last_nack = 0; /* zero out server ether in case the server ip has changed */ memset(net_server_ethaddr, 0, 6); /* Revert tftp_block_size to dflt */ @@ -796,6 +880,8 @@ void tftp_start_server(void) tftp_block_size = TFTP_BLOCK_SIZE; tftp_cur_block = 0; tftp_our_port = WELL_KNOWN_PORT; + tftp_windowsize = 1; + tftp_next_ack = tftp_windowsize; #ifdef CONFIG_TFTP_TSIZE tftp_tsize = 0; @@ -809,4 +895,3 @@ void tftp_start_server(void) memset(net_server_ethaddr, 0, 6); } #endif /* CONFIG_CMD_TFTPSRV */ -