18044SWilliam.Kucharski@Sun.COM /*
28044SWilliam.Kucharski@Sun.COM * GRUB -- GRand Unified Bootloader
38044SWilliam.Kucharski@Sun.COM * Copyright (C) 2000,2001,2002,2004 Free Software Foundation, Inc.
48044SWilliam.Kucharski@Sun.COM *
58044SWilliam.Kucharski@Sun.COM * This program is free software; you can redistribute it and/or modify
68044SWilliam.Kucharski@Sun.COM * it under the terms of the GNU General Public License as published by
78044SWilliam.Kucharski@Sun.COM * the Free Software Foundation; either version 2 of the License, or
88044SWilliam.Kucharski@Sun.COM * (at your option) any later version.
98044SWilliam.Kucharski@Sun.COM *
108044SWilliam.Kucharski@Sun.COM * This program is distributed in the hope that it will be useful,
118044SWilliam.Kucharski@Sun.COM * but WITHOUT ANY WARRANTY; without even the implied warranty of
128044SWilliam.Kucharski@Sun.COM * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
138044SWilliam.Kucharski@Sun.COM * GNU General Public License for more details.
148044SWilliam.Kucharski@Sun.COM *
158044SWilliam.Kucharski@Sun.COM * You should have received a copy of the GNU General Public License
168044SWilliam.Kucharski@Sun.COM * along with this program; if not, write to the Free Software
178044SWilliam.Kucharski@Sun.COM * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
188044SWilliam.Kucharski@Sun.COM */
198044SWilliam.Kucharski@Sun.COM
208044SWilliam.Kucharski@Sun.COM /* Based on "src/main.c" in etherboot-4.5.8. */
218044SWilliam.Kucharski@Sun.COM /**************************************************************************
228044SWilliam.Kucharski@Sun.COM ETHERBOOT - BOOTP/TFTP Bootstrap Program
238044SWilliam.Kucharski@Sun.COM
248044SWilliam.Kucharski@Sun.COM Author: Martin Renters
258044SWilliam.Kucharski@Sun.COM Date: Dec/93
268044SWilliam.Kucharski@Sun.COM
278044SWilliam.Kucharski@Sun.COM **************************************************************************/
288044SWilliam.Kucharski@Sun.COM
298044SWilliam.Kucharski@Sun.COM /* #define TFTP_DEBUG 1 */
308044SWilliam.Kucharski@Sun.COM
318044SWilliam.Kucharski@Sun.COM #include <filesys.h>
328044SWilliam.Kucharski@Sun.COM #include <shared.h>
338044SWilliam.Kucharski@Sun.COM
348044SWilliam.Kucharski@Sun.COM #include "grub.h"
358044SWilliam.Kucharski@Sun.COM #include "tftp.h"
368044SWilliam.Kucharski@Sun.COM #include "nic.h"
378044SWilliam.Kucharski@Sun.COM
388044SWilliam.Kucharski@Sun.COM static int tftp_file_read_undi(const char *name,
398044SWilliam.Kucharski@Sun.COM int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
408044SWilliam.Kucharski@Sun.COM static int tftp_read_undi(char *addr, int size);
418044SWilliam.Kucharski@Sun.COM static int tftp_dir_undi(char *dirname);
428044SWilliam.Kucharski@Sun.COM static void tftp_close_undi(void);
438044SWilliam.Kucharski@Sun.COM static int buf_fill_undi(int abort);
448044SWilliam.Kucharski@Sun.COM
458044SWilliam.Kucharski@Sun.COM extern int use_bios_pxe;
468044SWilliam.Kucharski@Sun.COM
478044SWilliam.Kucharski@Sun.COM static int retry;
488044SWilliam.Kucharski@Sun.COM static unsigned short iport = 2000;
498044SWilliam.Kucharski@Sun.COM static unsigned short oport = 0;
508044SWilliam.Kucharski@Sun.COM static unsigned short block, prevblock;
518044SWilliam.Kucharski@Sun.COM static int bcounter;
528044SWilliam.Kucharski@Sun.COM static struct tftp_t tp, saved_tp;
538044SWilliam.Kucharski@Sun.COM static int packetsize;
548044SWilliam.Kucharski@Sun.COM static int buf_eof, buf_read;
558044SWilliam.Kucharski@Sun.COM static int saved_filepos;
568044SWilliam.Kucharski@Sun.COM static unsigned short len, saved_len;
578044SWilliam.Kucharski@Sun.COM static char *buf, *saved_name;
588044SWilliam.Kucharski@Sun.COM
598044SWilliam.Kucharski@Sun.COM /**
608044SWilliam.Kucharski@Sun.COM * tftp_read
618044SWilliam.Kucharski@Sun.COM *
628044SWilliam.Kucharski@Sun.COM * Read file with _name_, data handled by _fnc_. In fact, grub never
638044SWilliam.Kucharski@Sun.COM * use it, we just use it to read dhcp config file.
648044SWilliam.Kucharski@Sun.COM */
await_tftp(int ival,void * ptr __unused,unsigned short ptype __unused,struct iphdr * ip,struct udphdr * udp)658044SWilliam.Kucharski@Sun.COM static int await_tftp(int ival, void *ptr __unused,
668044SWilliam.Kucharski@Sun.COM unsigned short ptype __unused, struct iphdr *ip,
678044SWilliam.Kucharski@Sun.COM struct udphdr *udp)
688044SWilliam.Kucharski@Sun.COM {
698044SWilliam.Kucharski@Sun.COM static int tftp_count = 0;
708044SWilliam.Kucharski@Sun.COM
718044SWilliam.Kucharski@Sun.COM if (!udp) {
728044SWilliam.Kucharski@Sun.COM return 0;
738044SWilliam.Kucharski@Sun.COM }
748044SWilliam.Kucharski@Sun.COM if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
758044SWilliam.Kucharski@Sun.COM return 0;
768044SWilliam.Kucharski@Sun.COM if (ntohs(udp->dest) != ival)
778044SWilliam.Kucharski@Sun.COM return 0;
788044SWilliam.Kucharski@Sun.COM tftp_count++; /* show progress */
798044SWilliam.Kucharski@Sun.COM if ((tftp_count % 1000) == 0)
808044SWilliam.Kucharski@Sun.COM printf(".");
818044SWilliam.Kucharski@Sun.COM return 1;
828044SWilliam.Kucharski@Sun.COM }
838044SWilliam.Kucharski@Sun.COM
tftp_file_read(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))848044SWilliam.Kucharski@Sun.COM int tftp_file_read(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
858044SWilliam.Kucharski@Sun.COM {
868044SWilliam.Kucharski@Sun.COM struct tftpreq_t tp;
878044SWilliam.Kucharski@Sun.COM struct tftp_t *tr;
888044SWilliam.Kucharski@Sun.COM int rc;
898044SWilliam.Kucharski@Sun.COM
908044SWilliam.Kucharski@Sun.COM if (use_bios_pxe)
918044SWilliam.Kucharski@Sun.COM return (tftp_file_read_undi(name, fnc));
928044SWilliam.Kucharski@Sun.COM
938044SWilliam.Kucharski@Sun.COM retry = 0;
948044SWilliam.Kucharski@Sun.COM block = 0;
958044SWilliam.Kucharski@Sun.COM prevblock = 0;
968044SWilliam.Kucharski@Sun.COM bcounter = 0;
978044SWilliam.Kucharski@Sun.COM
988044SWilliam.Kucharski@Sun.COM
998044SWilliam.Kucharski@Sun.COM rx_qdrain();
1008044SWilliam.Kucharski@Sun.COM
1018044SWilliam.Kucharski@Sun.COM tp.opcode = htons(TFTP_RRQ);
1028044SWilliam.Kucharski@Sun.COM /* Warning: the following assumes the layout of bootp_t.
1038044SWilliam.Kucharski@Sun.COM But that's fixed by the IP, UDP and BOOTP specs. */
1048044SWilliam.Kucharski@Sun.COM len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
1058044SWilliam.Kucharski@Sun.COM sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
1068044SWilliam.Kucharski@Sun.COM name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
1078044SWilliam.Kucharski@Sun.COM if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
1088044SWilliam.Kucharski@Sun.COM TFTP_PORT, len, &tp))
1098044SWilliam.Kucharski@Sun.COM return (0);
1108044SWilliam.Kucharski@Sun.COM for (;;)
1118044SWilliam.Kucharski@Sun.COM {
1128044SWilliam.Kucharski@Sun.COM long timeout;
1138044SWilliam.Kucharski@Sun.COM #ifdef CONGESTED
1148044SWilliam.Kucharski@Sun.COM timeout = rfc2131_sleep_interval(block?TFTP_REXMT: TIMEOUT, retry);
1158044SWilliam.Kucharski@Sun.COM #else
1168044SWilliam.Kucharski@Sun.COM timeout = rfc2131_sleep_interval(TIMEOUT, retry);
1178044SWilliam.Kucharski@Sun.COM #endif
1188044SWilliam.Kucharski@Sun.COM if (!await_reply(await_tftp, iport, NULL, timeout))
1198044SWilliam.Kucharski@Sun.COM {
1208044SWilliam.Kucharski@Sun.COM if (!block && retry++ < MAX_TFTP_RETRIES)
1218044SWilliam.Kucharski@Sun.COM { /* maybe initial request was lost */
1228044SWilliam.Kucharski@Sun.COM if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1238044SWilliam.Kucharski@Sun.COM ++iport, TFTP_PORT, len, &tp))
1248044SWilliam.Kucharski@Sun.COM return (0);
1258044SWilliam.Kucharski@Sun.COM continue;
1268044SWilliam.Kucharski@Sun.COM }
1278044SWilliam.Kucharski@Sun.COM #ifdef CONGESTED
1288044SWilliam.Kucharski@Sun.COM if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
1298044SWilliam.Kucharski@Sun.COM { /* we resend our last ack */
1308044SWilliam.Kucharski@Sun.COM #ifdef MDEBUG
1318044SWilliam.Kucharski@Sun.COM printf("<REXMT>\n");
1328044SWilliam.Kucharski@Sun.COM #endif
1338044SWilliam.Kucharski@Sun.COM udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1348044SWilliam.Kucharski@Sun.COM iport, oport,
1358044SWilliam.Kucharski@Sun.COM TFTP_MIN_PACKET, &tp);
1368044SWilliam.Kucharski@Sun.COM continue;
1378044SWilliam.Kucharski@Sun.COM }
1388044SWilliam.Kucharski@Sun.COM #endif
1398044SWilliam.Kucharski@Sun.COM break; /* timeout */
1408044SWilliam.Kucharski@Sun.COM }
1418044SWilliam.Kucharski@Sun.COM tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
1428044SWilliam.Kucharski@Sun.COM if (tr->opcode == ntohs(TFTP_ERROR))
1438044SWilliam.Kucharski@Sun.COM {
1448044SWilliam.Kucharski@Sun.COM printf("TFTP error %d (%s)\n",
1458044SWilliam.Kucharski@Sun.COM ntohs(tr->u.err.errcode),
1468044SWilliam.Kucharski@Sun.COM tr->u.err.errmsg);
1478044SWilliam.Kucharski@Sun.COM break;
1488044SWilliam.Kucharski@Sun.COM }
1498044SWilliam.Kucharski@Sun.COM
1508044SWilliam.Kucharski@Sun.COM if (tr->opcode == ntohs(TFTP_OACK)) {
1518044SWilliam.Kucharski@Sun.COM char *p = tr->u.oack.data, *e;
1528044SWilliam.Kucharski@Sun.COM
1538044SWilliam.Kucharski@Sun.COM if (prevblock) /* shouldn't happen */
1548044SWilliam.Kucharski@Sun.COM continue; /* ignore it */
1558044SWilliam.Kucharski@Sun.COM len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
1568044SWilliam.Kucharski@Sun.COM if (len > TFTP_MAX_PACKET)
1578044SWilliam.Kucharski@Sun.COM goto noak;
1588044SWilliam.Kucharski@Sun.COM e = p + len;
1598044SWilliam.Kucharski@Sun.COM while (*p != '\0' && p < e) {
1608044SWilliam.Kucharski@Sun.COM /* if (!strcasecmp("blksize", p)) { */
1618044SWilliam.Kucharski@Sun.COM if (!grub_strcmp("blksize", p)) {
1628044SWilliam.Kucharski@Sun.COM p += 8;
1638044SWilliam.Kucharski@Sun.COM /* if ((packetsize = strtoul(p, &p, 10)) < */
1648044SWilliam.Kucharski@Sun.COM if ((packetsize = getdec(&p)) < TFTP_DEFAULTSIZE_PACKET)
1658044SWilliam.Kucharski@Sun.COM goto noak;
1668044SWilliam.Kucharski@Sun.COM while (p < e && *p) p++;
1678044SWilliam.Kucharski@Sun.COM if (p < e)
1688044SWilliam.Kucharski@Sun.COM p++;
1698044SWilliam.Kucharski@Sun.COM }
1708044SWilliam.Kucharski@Sun.COM else {
1718044SWilliam.Kucharski@Sun.COM noak:
1728044SWilliam.Kucharski@Sun.COM tp.opcode = htons(TFTP_ERROR);
1738044SWilliam.Kucharski@Sun.COM tp.u.err.errcode = 8;
1748044SWilliam.Kucharski@Sun.COM /*
1758044SWilliam.Kucharski@Sun.COM * Warning: the following assumes the layout of bootp_t.
1768044SWilliam.Kucharski@Sun.COM * But that's fixed by the IP, UDP and BOOTP specs.
1778044SWilliam.Kucharski@Sun.COM */
1788044SWilliam.Kucharski@Sun.COM len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) +
1798044SWilliam.Kucharski@Sun.COM /*
1808044SWilliam.Kucharski@Sun.COM * Normally bad form to omit the format string, but in this case
1818044SWilliam.Kucharski@Sun.COM * the string we are copying from is fixed. sprintf is just being
1828044SWilliam.Kucharski@Sun.COM * used as a strcpy and strlen.
1838044SWilliam.Kucharski@Sun.COM */
1848044SWilliam.Kucharski@Sun.COM sprintf((char *)tp.u.err.errmsg,
1858044SWilliam.Kucharski@Sun.COM "RFC1782 error") + 1;
1868044SWilliam.Kucharski@Sun.COM udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1878044SWilliam.Kucharski@Sun.COM iport, ntohs(tr->udp.src),
1888044SWilliam.Kucharski@Sun.COM len, &tp);
1898044SWilliam.Kucharski@Sun.COM return (0);
1908044SWilliam.Kucharski@Sun.COM }
1918044SWilliam.Kucharski@Sun.COM }
1928044SWilliam.Kucharski@Sun.COM if (p > e)
1938044SWilliam.Kucharski@Sun.COM goto noak;
1948044SWilliam.Kucharski@Sun.COM block = tp.u.ack.block = 0; /* this ensures, that */
1958044SWilliam.Kucharski@Sun.COM /* the packet does not get */
1968044SWilliam.Kucharski@Sun.COM /* processed as data! */
1978044SWilliam.Kucharski@Sun.COM }
1988044SWilliam.Kucharski@Sun.COM else if (tr->opcode == htons(TFTP_DATA)) {
1998044SWilliam.Kucharski@Sun.COM len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
2008044SWilliam.Kucharski@Sun.COM if (len > packetsize) /* shouldn't happen */
2018044SWilliam.Kucharski@Sun.COM continue; /* ignore it */
2028044SWilliam.Kucharski@Sun.COM block = ntohs(tp.u.ack.block = tr->u.data.block); }
2038044SWilliam.Kucharski@Sun.COM else {/* neither TFTP_OACK nor TFTP_DATA */
2048044SWilliam.Kucharski@Sun.COM break;
2058044SWilliam.Kucharski@Sun.COM }
2068044SWilliam.Kucharski@Sun.COM
2078044SWilliam.Kucharski@Sun.COM if ((block || bcounter) && (block != (unsigned short)(prevblock+1))) {
2088044SWilliam.Kucharski@Sun.COM /* Block order should be continuous */
2098044SWilliam.Kucharski@Sun.COM tp.u.ack.block = htons(block = prevblock);
2108044SWilliam.Kucharski@Sun.COM }
2118044SWilliam.Kucharski@Sun.COM tp.opcode = htons(TFTP_ACK);
2128044SWilliam.Kucharski@Sun.COM oport = ntohs(tr->udp.src);
2138044SWilliam.Kucharski@Sun.COM udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
2148044SWilliam.Kucharski@Sun.COM oport, TFTP_MIN_PACKET, &tp); /* ack */
2158044SWilliam.Kucharski@Sun.COM if ((unsigned short)(block-prevblock) != 1) {
2168044SWilliam.Kucharski@Sun.COM /* Retransmission or OACK, don't process via callback
2178044SWilliam.Kucharski@Sun.COM * and don't change the value of prevblock. */
2188044SWilliam.Kucharski@Sun.COM continue;
2198044SWilliam.Kucharski@Sun.COM }
2208044SWilliam.Kucharski@Sun.COM prevblock = block;
2218044SWilliam.Kucharski@Sun.COM retry = 0; /* It's the right place to zero the timer? */
2228044SWilliam.Kucharski@Sun.COM if ((rc = fnc(tr->u.data.download,
2238044SWilliam.Kucharski@Sun.COM ++bcounter, len, len < packetsize)) <= 0)
2248044SWilliam.Kucharski@Sun.COM return(rc);
2258044SWilliam.Kucharski@Sun.COM if (len < packetsize) { /* End of data --- fnc should not have returned */
2268044SWilliam.Kucharski@Sun.COM printf("tftp download complete, but\n");
2278044SWilliam.Kucharski@Sun.COM return (1);
2288044SWilliam.Kucharski@Sun.COM }
2298044SWilliam.Kucharski@Sun.COM }
2308044SWilliam.Kucharski@Sun.COM return (0);
2318044SWilliam.Kucharski@Sun.COM }
2328044SWilliam.Kucharski@Sun.COM
2338044SWilliam.Kucharski@Sun.COM /* Fill the buffer by receiving the data via the TFTP protocol. */
2348044SWilliam.Kucharski@Sun.COM static int
buf_fill(int abort)2358044SWilliam.Kucharski@Sun.COM buf_fill (int abort)
2368044SWilliam.Kucharski@Sun.COM {
2378044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
2388044SWilliam.Kucharski@Sun.COM grub_printf ("buf_fill (%d)\n", abort);
2398044SWilliam.Kucharski@Sun.COM #endif
2408044SWilliam.Kucharski@Sun.COM
2418044SWilliam.Kucharski@Sun.COM if (use_bios_pxe)
2428044SWilliam.Kucharski@Sun.COM return (buf_fill_undi(abort));
2438044SWilliam.Kucharski@Sun.COM
2448044SWilliam.Kucharski@Sun.COM while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
2458044SWilliam.Kucharski@Sun.COM {
2468044SWilliam.Kucharski@Sun.COM struct tftp_t *tr;
2478044SWilliam.Kucharski@Sun.COM long timeout;
2488044SWilliam.Kucharski@Sun.COM
2498044SWilliam.Kucharski@Sun.COM #ifdef CONGESTED
2508044SWilliam.Kucharski@Sun.COM timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
2518044SWilliam.Kucharski@Sun.COM #else
2528044SWilliam.Kucharski@Sun.COM timeout = rfc2131_sleep_interval (TIMEOUT, retry);
2538044SWilliam.Kucharski@Sun.COM #endif
2548044SWilliam.Kucharski@Sun.COM
2558044SWilliam.Kucharski@Sun.COM if (! await_reply (await_tftp, iport, NULL, timeout))
2568044SWilliam.Kucharski@Sun.COM {
2578044SWilliam.Kucharski@Sun.COM if (user_abort)
2588044SWilliam.Kucharski@Sun.COM return 0;
2598044SWilliam.Kucharski@Sun.COM
2608044SWilliam.Kucharski@Sun.COM if (! block && retry++ < MAX_TFTP_RETRIES)
2618044SWilliam.Kucharski@Sun.COM {
2628044SWilliam.Kucharski@Sun.COM /* Maybe initial request was lost. */
2638044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
2648044SWilliam.Kucharski@Sun.COM grub_printf ("Maybe initial request was lost.\n");
2658044SWilliam.Kucharski@Sun.COM #endif
2668044SWilliam.Kucharski@Sun.COM if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2678044SWilliam.Kucharski@Sun.COM ++iport, TFTP_PORT, len, &tp))
2688044SWilliam.Kucharski@Sun.COM return 0;
2698044SWilliam.Kucharski@Sun.COM
2708044SWilliam.Kucharski@Sun.COM continue;
2718044SWilliam.Kucharski@Sun.COM }
2728044SWilliam.Kucharski@Sun.COM
2738044SWilliam.Kucharski@Sun.COM #ifdef CONGESTED
2748044SWilliam.Kucharski@Sun.COM if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
2758044SWilliam.Kucharski@Sun.COM {
2768044SWilliam.Kucharski@Sun.COM /* We resend our last ack. */
2778044SWilliam.Kucharski@Sun.COM # ifdef TFTP_DEBUG
2788044SWilliam.Kucharski@Sun.COM grub_printf ("<REXMT>\n");
2798044SWilliam.Kucharski@Sun.COM # endif
2808044SWilliam.Kucharski@Sun.COM udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2818044SWilliam.Kucharski@Sun.COM iport, oport,
2828044SWilliam.Kucharski@Sun.COM TFTP_MIN_PACKET, &tp);
2838044SWilliam.Kucharski@Sun.COM continue;
2848044SWilliam.Kucharski@Sun.COM }
2858044SWilliam.Kucharski@Sun.COM #endif
2868044SWilliam.Kucharski@Sun.COM /* Timeout. */
2878044SWilliam.Kucharski@Sun.COM return 0;
2888044SWilliam.Kucharski@Sun.COM }
2898044SWilliam.Kucharski@Sun.COM
2908044SWilliam.Kucharski@Sun.COM tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
2918044SWilliam.Kucharski@Sun.COM if (tr->opcode == ntohs (TFTP_ERROR))
2928044SWilliam.Kucharski@Sun.COM {
2938044SWilliam.Kucharski@Sun.COM grub_printf ("TFTP error %d (%s)\n",
2948044SWilliam.Kucharski@Sun.COM ntohs (tr->u.err.errcode),
2958044SWilliam.Kucharski@Sun.COM tr->u.err.errmsg);
2968044SWilliam.Kucharski@Sun.COM return 0;
2978044SWilliam.Kucharski@Sun.COM }
2988044SWilliam.Kucharski@Sun.COM
2998044SWilliam.Kucharski@Sun.COM if (tr->opcode == ntohs (TFTP_OACK))
3008044SWilliam.Kucharski@Sun.COM {
3018044SWilliam.Kucharski@Sun.COM char *p = tr->u.oack.data, *e;
3028044SWilliam.Kucharski@Sun.COM
3038044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3048044SWilliam.Kucharski@Sun.COM grub_printf ("OACK ");
3058044SWilliam.Kucharski@Sun.COM #endif
3068044SWilliam.Kucharski@Sun.COM /* Shouldn't happen. */
3078044SWilliam.Kucharski@Sun.COM if (prevblock)
3088044SWilliam.Kucharski@Sun.COM {
3098044SWilliam.Kucharski@Sun.COM /* Ignore it. */
3108044SWilliam.Kucharski@Sun.COM grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
3118044SWilliam.Kucharski@Sun.COM __FILE__, __LINE__, prevblock);
3128044SWilliam.Kucharski@Sun.COM continue;
3138044SWilliam.Kucharski@Sun.COM }
3148044SWilliam.Kucharski@Sun.COM
3158044SWilliam.Kucharski@Sun.COM len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
3168044SWilliam.Kucharski@Sun.COM if (len > TFTP_MAX_PACKET)
3178044SWilliam.Kucharski@Sun.COM goto noak;
3188044SWilliam.Kucharski@Sun.COM
3198044SWilliam.Kucharski@Sun.COM e = p + len;
3208044SWilliam.Kucharski@Sun.COM while (*p != '\000' && p < e)
3218044SWilliam.Kucharski@Sun.COM {
3228044SWilliam.Kucharski@Sun.COM if (! grub_strcmp ("blksize", p))
3238044SWilliam.Kucharski@Sun.COM {
3248044SWilliam.Kucharski@Sun.COM p += 8;
3258044SWilliam.Kucharski@Sun.COM if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
3268044SWilliam.Kucharski@Sun.COM goto noak;
3278044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3288044SWilliam.Kucharski@Sun.COM grub_printf ("blksize = %d\n", packetsize);
3298044SWilliam.Kucharski@Sun.COM #endif
3308044SWilliam.Kucharski@Sun.COM }
3318044SWilliam.Kucharski@Sun.COM else if (! grub_strcmp ("tsize", p))
3328044SWilliam.Kucharski@Sun.COM {
3338044SWilliam.Kucharski@Sun.COM p += 6;
3348044SWilliam.Kucharski@Sun.COM if ((filemax = getdec (&p)) < 0)
3358044SWilliam.Kucharski@Sun.COM {
3368044SWilliam.Kucharski@Sun.COM filemax = -1;
3378044SWilliam.Kucharski@Sun.COM goto noak;
3388044SWilliam.Kucharski@Sun.COM }
3398044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3408044SWilliam.Kucharski@Sun.COM grub_printf ("tsize = %d\n", filemax);
3418044SWilliam.Kucharski@Sun.COM #endif
3428044SWilliam.Kucharski@Sun.COM }
3438044SWilliam.Kucharski@Sun.COM else
3448044SWilliam.Kucharski@Sun.COM {
3458044SWilliam.Kucharski@Sun.COM noak:
3468044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3478044SWilliam.Kucharski@Sun.COM grub_printf ("NOAK\n");
3488044SWilliam.Kucharski@Sun.COM #endif
3498044SWilliam.Kucharski@Sun.COM tp.opcode = htons (TFTP_ERROR);
3508044SWilliam.Kucharski@Sun.COM tp.u.err.errcode = 8;
3518044SWilliam.Kucharski@Sun.COM len = (grub_sprintf ((char *) tp.u.err.errmsg,
3528044SWilliam.Kucharski@Sun.COM "RFC1782 error")
3538044SWilliam.Kucharski@Sun.COM + sizeof (tp.ip) + sizeof (tp.udp)
3548044SWilliam.Kucharski@Sun.COM + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
3558044SWilliam.Kucharski@Sun.COM + 1);
3568044SWilliam.Kucharski@Sun.COM udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
3578044SWilliam.Kucharski@Sun.COM iport, ntohs (tr->udp.src),
3588044SWilliam.Kucharski@Sun.COM len, &tp);
3598044SWilliam.Kucharski@Sun.COM return 0;
3608044SWilliam.Kucharski@Sun.COM }
3618044SWilliam.Kucharski@Sun.COM
3628044SWilliam.Kucharski@Sun.COM while (p < e && *p)
3638044SWilliam.Kucharski@Sun.COM p++;
3648044SWilliam.Kucharski@Sun.COM
3658044SWilliam.Kucharski@Sun.COM if (p < e)
3668044SWilliam.Kucharski@Sun.COM p++;
3678044SWilliam.Kucharski@Sun.COM }
3688044SWilliam.Kucharski@Sun.COM
3698044SWilliam.Kucharski@Sun.COM if (p > e)
3708044SWilliam.Kucharski@Sun.COM goto noak;
3718044SWilliam.Kucharski@Sun.COM
3728044SWilliam.Kucharski@Sun.COM /* This ensures that the packet does not get processed as
3738044SWilliam.Kucharski@Sun.COM data! */
3748044SWilliam.Kucharski@Sun.COM block = tp.u.ack.block = 0;
3758044SWilliam.Kucharski@Sun.COM }
3768044SWilliam.Kucharski@Sun.COM else if (tr->opcode == ntohs (TFTP_DATA))
3778044SWilliam.Kucharski@Sun.COM {
3788044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
3798044SWilliam.Kucharski@Sun.COM grub_printf ("DATA ");
3808044SWilliam.Kucharski@Sun.COM #endif
3818044SWilliam.Kucharski@Sun.COM len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
3828044SWilliam.Kucharski@Sun.COM
3838044SWilliam.Kucharski@Sun.COM /* Shouldn't happen. */
3848044SWilliam.Kucharski@Sun.COM if (len > packetsize)
3858044SWilliam.Kucharski@Sun.COM {
3868044SWilliam.Kucharski@Sun.COM /* Ignore it. */
3878044SWilliam.Kucharski@Sun.COM grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
3888044SWilliam.Kucharski@Sun.COM __FILE__, __LINE__, len, packetsize);
3898044SWilliam.Kucharski@Sun.COM continue;
3908044SWilliam.Kucharski@Sun.COM }
3918044SWilliam.Kucharski@Sun.COM
3928044SWilliam.Kucharski@Sun.COM block = ntohs (tp.u.ack.block = tr->u.data.block);
3938044SWilliam.Kucharski@Sun.COM }
3948044SWilliam.Kucharski@Sun.COM else
3958044SWilliam.Kucharski@Sun.COM /* Neither TFTP_OACK nor TFTP_DATA. */
3968044SWilliam.Kucharski@Sun.COM break;
3978044SWilliam.Kucharski@Sun.COM
398*10708SKerry.Shu@Sun.COM if ((block || bcounter) && (block != (unsigned short) (prevblock + 1)))
3998044SWilliam.Kucharski@Sun.COM /* Block order should be continuous */
4008044SWilliam.Kucharski@Sun.COM tp.u.ack.block = htons (block = prevblock);
4018044SWilliam.Kucharski@Sun.COM
4028044SWilliam.Kucharski@Sun.COM /* Should be continuous. */
4038044SWilliam.Kucharski@Sun.COM tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
4048044SWilliam.Kucharski@Sun.COM oport = ntohs (tr->udp.src);
4058044SWilliam.Kucharski@Sun.COM
4068044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
4078044SWilliam.Kucharski@Sun.COM grub_printf ("ACK\n");
4088044SWilliam.Kucharski@Sun.COM #endif
4098044SWilliam.Kucharski@Sun.COM /* Ack. */
4108044SWilliam.Kucharski@Sun.COM udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
4118044SWilliam.Kucharski@Sun.COM oport, TFTP_MIN_PACKET, &tp);
4128044SWilliam.Kucharski@Sun.COM
4138044SWilliam.Kucharski@Sun.COM if (abort)
4148044SWilliam.Kucharski@Sun.COM {
4158044SWilliam.Kucharski@Sun.COM buf_eof = 1;
4168044SWilliam.Kucharski@Sun.COM break;
4178044SWilliam.Kucharski@Sun.COM }
4188044SWilliam.Kucharski@Sun.COM
4198044SWilliam.Kucharski@Sun.COM /* Retransmission or OACK. */
4208044SWilliam.Kucharski@Sun.COM if ((unsigned short) (block - prevblock) != 1)
4218044SWilliam.Kucharski@Sun.COM /* Don't process. */
4228044SWilliam.Kucharski@Sun.COM continue;
4238044SWilliam.Kucharski@Sun.COM
4248044SWilliam.Kucharski@Sun.COM prevblock = block;
4258044SWilliam.Kucharski@Sun.COM /* Is it the right place to zero the timer? */
4268044SWilliam.Kucharski@Sun.COM retry = 0;
4278044SWilliam.Kucharski@Sun.COM
4288044SWilliam.Kucharski@Sun.COM /* In GRUB, this variable doesn't play any important role at all,
4298044SWilliam.Kucharski@Sun.COM but use it for consistency with Etherboot. */
4308044SWilliam.Kucharski@Sun.COM bcounter++;
4318044SWilliam.Kucharski@Sun.COM
4328044SWilliam.Kucharski@Sun.COM /* Copy the downloaded data to the buffer. */
4338044SWilliam.Kucharski@Sun.COM grub_memmove (buf + buf_read, tr->u.data.download, len);
4348044SWilliam.Kucharski@Sun.COM buf_read += len;
4358044SWilliam.Kucharski@Sun.COM
4368044SWilliam.Kucharski@Sun.COM /* End of data. */
4378044SWilliam.Kucharski@Sun.COM if (len < packetsize)
4388044SWilliam.Kucharski@Sun.COM buf_eof = 1;
4398044SWilliam.Kucharski@Sun.COM }
4408044SWilliam.Kucharski@Sun.COM
4418044SWilliam.Kucharski@Sun.COM return 1;
4428044SWilliam.Kucharski@Sun.COM }
4438044SWilliam.Kucharski@Sun.COM
4448044SWilliam.Kucharski@Sun.COM /* Send the RRQ whose length is LEN. */
4458044SWilliam.Kucharski@Sun.COM static int
send_rrq(void)4468044SWilliam.Kucharski@Sun.COM send_rrq (void)
4478044SWilliam.Kucharski@Sun.COM {
4488044SWilliam.Kucharski@Sun.COM /* Initialize some variables. */
4498044SWilliam.Kucharski@Sun.COM retry = 0;
4508044SWilliam.Kucharski@Sun.COM block = 0;
4518044SWilliam.Kucharski@Sun.COM prevblock = 0;
4528044SWilliam.Kucharski@Sun.COM packetsize = TFTP_DEFAULTSIZE_PACKET;
4538044SWilliam.Kucharski@Sun.COM bcounter = 0;
4548044SWilliam.Kucharski@Sun.COM
4558044SWilliam.Kucharski@Sun.COM buf = (char *) FSYS_BUF;
4568044SWilliam.Kucharski@Sun.COM buf_eof = 0;
4578044SWilliam.Kucharski@Sun.COM buf_read = 0;
4588044SWilliam.Kucharski@Sun.COM saved_filepos = 0;
4598044SWilliam.Kucharski@Sun.COM
4608044SWilliam.Kucharski@Sun.COM rx_qdrain();
4618044SWilliam.Kucharski@Sun.COM
4628044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
4638044SWilliam.Kucharski@Sun.COM grub_printf ("send_rrq ()\n");
4648044SWilliam.Kucharski@Sun.COM {
4658044SWilliam.Kucharski@Sun.COM int i;
4668044SWilliam.Kucharski@Sun.COM char *p;
4678044SWilliam.Kucharski@Sun.COM
4688044SWilliam.Kucharski@Sun.COM for (i = 0, p = (char *) &tp; i < len; i++)
4698044SWilliam.Kucharski@Sun.COM if (p[i] >= ' ' && p[i] <= '~')
4708044SWilliam.Kucharski@Sun.COM grub_putchar (p[i]);
4718044SWilliam.Kucharski@Sun.COM else
4728044SWilliam.Kucharski@Sun.COM grub_printf ("\\%x", (unsigned) p[i]);
4738044SWilliam.Kucharski@Sun.COM
4748044SWilliam.Kucharski@Sun.COM grub_putchar ('\n');
4758044SWilliam.Kucharski@Sun.COM }
4768044SWilliam.Kucharski@Sun.COM #endif
4778044SWilliam.Kucharski@Sun.COM /* Send the packet. */
4788044SWilliam.Kucharski@Sun.COM return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
4798044SWilliam.Kucharski@Sun.COM TFTP_PORT, len, &tp);
4808044SWilliam.Kucharski@Sun.COM }
4818044SWilliam.Kucharski@Sun.COM
4828044SWilliam.Kucharski@Sun.COM /* Mount the network drive. If the drive is ready, return one, otherwise
4838044SWilliam.Kucharski@Sun.COM return zero. */
4848044SWilliam.Kucharski@Sun.COM int
tftp_mount(void)4858044SWilliam.Kucharski@Sun.COM tftp_mount (void)
4868044SWilliam.Kucharski@Sun.COM {
4878044SWilliam.Kucharski@Sun.COM /* Check if the current drive is the network drive. */
4888044SWilliam.Kucharski@Sun.COM if (current_drive != NETWORK_DRIVE)
4898044SWilliam.Kucharski@Sun.COM return 0;
4908044SWilliam.Kucharski@Sun.COM
4918044SWilliam.Kucharski@Sun.COM /* If the drive is not initialized yet, abort. */
4928044SWilliam.Kucharski@Sun.COM if (! network_ready)
4938044SWilliam.Kucharski@Sun.COM return 0;
4948044SWilliam.Kucharski@Sun.COM
4958044SWilliam.Kucharski@Sun.COM return 1;
4968044SWilliam.Kucharski@Sun.COM }
4978044SWilliam.Kucharski@Sun.COM
4988044SWilliam.Kucharski@Sun.COM /* Read up to SIZE bytes, returned in ADDR. */
4998044SWilliam.Kucharski@Sun.COM int
tftp_read(char * addr,int size)5008044SWilliam.Kucharski@Sun.COM tftp_read (char *addr, int size)
5018044SWilliam.Kucharski@Sun.COM {
5028044SWilliam.Kucharski@Sun.COM /* How many bytes is read? */
5038044SWilliam.Kucharski@Sun.COM int ret = 0;
5048044SWilliam.Kucharski@Sun.COM
5058044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
5068044SWilliam.Kucharski@Sun.COM grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
5078044SWilliam.Kucharski@Sun.COM #endif
5088044SWilliam.Kucharski@Sun.COM
5098044SWilliam.Kucharski@Sun.COM if (use_bios_pxe)
5108044SWilliam.Kucharski@Sun.COM return (tftp_read_undi(addr, size));
5118044SWilliam.Kucharski@Sun.COM
5128044SWilliam.Kucharski@Sun.COM if (filepos < saved_filepos)
5138044SWilliam.Kucharski@Sun.COM {
5148044SWilliam.Kucharski@Sun.COM /* Uggh.. FILEPOS has been moved backwards. So reopen the file. */
5158044SWilliam.Kucharski@Sun.COM buf_read = 0;
5168044SWilliam.Kucharski@Sun.COM buf_fill (1);
5178044SWilliam.Kucharski@Sun.COM grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
5188044SWilliam.Kucharski@Sun.COM len = saved_len;
5198044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
5208044SWilliam.Kucharski@Sun.COM {
5218044SWilliam.Kucharski@Sun.COM int i;
5228044SWilliam.Kucharski@Sun.COM grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
5238044SWilliam.Kucharski@Sun.COM for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
5248044SWilliam.Kucharski@Sun.COM {
5258044SWilliam.Kucharski@Sun.COM if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
5268044SWilliam.Kucharski@Sun.COM grub_putchar (tp.u.rrq[i]);
5278044SWilliam.Kucharski@Sun.COM else
5288044SWilliam.Kucharski@Sun.COM grub_putchar ('*');
5298044SWilliam.Kucharski@Sun.COM }
5308044SWilliam.Kucharski@Sun.COM grub_putchar ('\n');
5318044SWilliam.Kucharski@Sun.COM }
5328044SWilliam.Kucharski@Sun.COM #endif
5338044SWilliam.Kucharski@Sun.COM
5348044SWilliam.Kucharski@Sun.COM if (! send_rrq ())
5358044SWilliam.Kucharski@Sun.COM {
5368044SWilliam.Kucharski@Sun.COM errnum = ERR_WRITE;
5378044SWilliam.Kucharski@Sun.COM return 0;
5388044SWilliam.Kucharski@Sun.COM }
5398044SWilliam.Kucharski@Sun.COM }
5408044SWilliam.Kucharski@Sun.COM
5418044SWilliam.Kucharski@Sun.COM while (size > 0)
5428044SWilliam.Kucharski@Sun.COM {
5438044SWilliam.Kucharski@Sun.COM int amt = buf_read + saved_filepos - filepos;
5448044SWilliam.Kucharski@Sun.COM
5458044SWilliam.Kucharski@Sun.COM /* If the length that can be copied from the buffer is over the
5468044SWilliam.Kucharski@Sun.COM requested size, cut it down. */
5478044SWilliam.Kucharski@Sun.COM if (amt > size)
5488044SWilliam.Kucharski@Sun.COM amt = size;
5498044SWilliam.Kucharski@Sun.COM
5508044SWilliam.Kucharski@Sun.COM if (amt > 0)
5518044SWilliam.Kucharski@Sun.COM {
5528044SWilliam.Kucharski@Sun.COM /* Copy the buffer to the supplied memory space. */
5538044SWilliam.Kucharski@Sun.COM grub_memmove (addr, buf + filepos - saved_filepos, amt);
5548044SWilliam.Kucharski@Sun.COM size -= amt;
5558044SWilliam.Kucharski@Sun.COM addr += amt;
5568044SWilliam.Kucharski@Sun.COM filepos += amt;
5578044SWilliam.Kucharski@Sun.COM ret += amt;
5588044SWilliam.Kucharski@Sun.COM
5598044SWilliam.Kucharski@Sun.COM /* If the size of the empty space becomes small, move the unused
5608044SWilliam.Kucharski@Sun.COM data forwards. */
5618044SWilliam.Kucharski@Sun.COM if (filepos - saved_filepos > FSYS_BUFLEN / 2)
5628044SWilliam.Kucharski@Sun.COM {
5638044SWilliam.Kucharski@Sun.COM grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
5648044SWilliam.Kucharski@Sun.COM buf_read -= FSYS_BUFLEN / 2;
5658044SWilliam.Kucharski@Sun.COM saved_filepos += FSYS_BUFLEN / 2;
5668044SWilliam.Kucharski@Sun.COM }
5678044SWilliam.Kucharski@Sun.COM }
5688044SWilliam.Kucharski@Sun.COM else
5698044SWilliam.Kucharski@Sun.COM {
5708044SWilliam.Kucharski@Sun.COM /* Skip the whole buffer. */
5718044SWilliam.Kucharski@Sun.COM saved_filepos += buf_read;
5728044SWilliam.Kucharski@Sun.COM buf_read = 0;
5738044SWilliam.Kucharski@Sun.COM }
5748044SWilliam.Kucharski@Sun.COM
5758044SWilliam.Kucharski@Sun.COM /* Read the data. */
5768044SWilliam.Kucharski@Sun.COM if (size > 0 && ! buf_fill (0))
5778044SWilliam.Kucharski@Sun.COM {
5788044SWilliam.Kucharski@Sun.COM errnum = ERR_READ;
5798044SWilliam.Kucharski@Sun.COM return 0;
5808044SWilliam.Kucharski@Sun.COM }
5818044SWilliam.Kucharski@Sun.COM
5828044SWilliam.Kucharski@Sun.COM /* Sanity check. */
5838044SWilliam.Kucharski@Sun.COM if (size > 0 && buf_read == 0)
5848044SWilliam.Kucharski@Sun.COM {
5858044SWilliam.Kucharski@Sun.COM errnum = ERR_READ;
5868044SWilliam.Kucharski@Sun.COM return 0;
5878044SWilliam.Kucharski@Sun.COM }
5888044SWilliam.Kucharski@Sun.COM }
5898044SWilliam.Kucharski@Sun.COM
5908044SWilliam.Kucharski@Sun.COM return ret;
5918044SWilliam.Kucharski@Sun.COM }
5928044SWilliam.Kucharski@Sun.COM
5938044SWilliam.Kucharski@Sun.COM /* Check if the file DIRNAME really exists. Get the size and save it in
5948044SWilliam.Kucharski@Sun.COM FILEMAX. */
5958044SWilliam.Kucharski@Sun.COM int
tftp_dir(char * dirname)5968044SWilliam.Kucharski@Sun.COM tftp_dir (char *dirname)
5978044SWilliam.Kucharski@Sun.COM {
5988044SWilliam.Kucharski@Sun.COM int ch;
5998044SWilliam.Kucharski@Sun.COM
6008044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
6018044SWilliam.Kucharski@Sun.COM grub_printf ("tftp_dir (%s)\n", dirname);
6028044SWilliam.Kucharski@Sun.COM #endif
6038044SWilliam.Kucharski@Sun.COM
6048044SWilliam.Kucharski@Sun.COM if (use_bios_pxe)
6058044SWilliam.Kucharski@Sun.COM return (tftp_dir_undi(dirname));
6068044SWilliam.Kucharski@Sun.COM
6078044SWilliam.Kucharski@Sun.COM /* In TFTP, there is no way to know what files exist. */
6088044SWilliam.Kucharski@Sun.COM if (print_possibilities)
6098044SWilliam.Kucharski@Sun.COM return 1;
6108044SWilliam.Kucharski@Sun.COM
6118044SWilliam.Kucharski@Sun.COM /* Don't know the size yet. */
6128044SWilliam.Kucharski@Sun.COM filemax = -1;
6138044SWilliam.Kucharski@Sun.COM
6148044SWilliam.Kucharski@Sun.COM reopen:
6158044SWilliam.Kucharski@Sun.COM /* Construct the TFTP request packet. */
6168044SWilliam.Kucharski@Sun.COM tp.opcode = htons (TFTP_RRQ);
6178044SWilliam.Kucharski@Sun.COM /* Terminate the filename. */
6188044SWilliam.Kucharski@Sun.COM ch = nul_terminate (dirname);
6198044SWilliam.Kucharski@Sun.COM /* Make the request string (octet, blksize and tsize). */
6208044SWilliam.Kucharski@Sun.COM len = (grub_sprintf ((char *) tp.u.rrq,
6218044SWilliam.Kucharski@Sun.COM "%s%coctet%cblksize%c%d%ctsize%c0",
6228044SWilliam.Kucharski@Sun.COM dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
6238044SWilliam.Kucharski@Sun.COM + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
6248044SWilliam.Kucharski@Sun.COM /* Restore the original DIRNAME. */
6258044SWilliam.Kucharski@Sun.COM dirname[grub_strlen (dirname)] = ch;
6268044SWilliam.Kucharski@Sun.COM /* Save the TFTP packet so that we can reopen the file later. */
6278044SWilliam.Kucharski@Sun.COM grub_memmove ((char *) &saved_tp, (char *) &tp, len);
6288044SWilliam.Kucharski@Sun.COM saved_len = len;
6298044SWilliam.Kucharski@Sun.COM if (! send_rrq ())
6308044SWilliam.Kucharski@Sun.COM {
6318044SWilliam.Kucharski@Sun.COM errnum = ERR_WRITE;
6328044SWilliam.Kucharski@Sun.COM return 0;
6338044SWilliam.Kucharski@Sun.COM }
6348044SWilliam.Kucharski@Sun.COM
6358044SWilliam.Kucharski@Sun.COM /* Read the data. */
6368044SWilliam.Kucharski@Sun.COM if (! buf_fill (0))
6378044SWilliam.Kucharski@Sun.COM {
6388044SWilliam.Kucharski@Sun.COM errnum = ERR_FILE_NOT_FOUND;
6398044SWilliam.Kucharski@Sun.COM return 0;
6408044SWilliam.Kucharski@Sun.COM }
6418044SWilliam.Kucharski@Sun.COM
6428044SWilliam.Kucharski@Sun.COM if (filemax == -1)
6438044SWilliam.Kucharski@Sun.COM {
6448044SWilliam.Kucharski@Sun.COM /* The server doesn't support the "tsize" option, so we must read
6458044SWilliam.Kucharski@Sun.COM the file twice... */
6468044SWilliam.Kucharski@Sun.COM
6478044SWilliam.Kucharski@Sun.COM /* Zero the size of the file. */
6488044SWilliam.Kucharski@Sun.COM filemax = 0;
6498044SWilliam.Kucharski@Sun.COM do
6508044SWilliam.Kucharski@Sun.COM {
6518044SWilliam.Kucharski@Sun.COM /* Add the length of the downloaded data. */
6528044SWilliam.Kucharski@Sun.COM filemax += buf_read;
6538044SWilliam.Kucharski@Sun.COM /* Reset the offset. Just discard the contents of the buffer. */
6548044SWilliam.Kucharski@Sun.COM buf_read = 0;
6558044SWilliam.Kucharski@Sun.COM /* Read the data. */
6568044SWilliam.Kucharski@Sun.COM if (! buf_fill (0))
6578044SWilliam.Kucharski@Sun.COM {
6588044SWilliam.Kucharski@Sun.COM errnum = ERR_READ;
6598044SWilliam.Kucharski@Sun.COM return 0;
6608044SWilliam.Kucharski@Sun.COM }
6618044SWilliam.Kucharski@Sun.COM }
6628044SWilliam.Kucharski@Sun.COM while (! buf_eof);
6638044SWilliam.Kucharski@Sun.COM
6648044SWilliam.Kucharski@Sun.COM /* Maybe a few amounts of data remains. */
6658044SWilliam.Kucharski@Sun.COM filemax += buf_read;
6668044SWilliam.Kucharski@Sun.COM
6678044SWilliam.Kucharski@Sun.COM /* Retry the open instruction. */
6688044SWilliam.Kucharski@Sun.COM goto reopen;
6698044SWilliam.Kucharski@Sun.COM }
6708044SWilliam.Kucharski@Sun.COM
6718044SWilliam.Kucharski@Sun.COM return 1;
6728044SWilliam.Kucharski@Sun.COM }
6738044SWilliam.Kucharski@Sun.COM
6748044SWilliam.Kucharski@Sun.COM /* Close the file. */
6758044SWilliam.Kucharski@Sun.COM void
tftp_close(void)6768044SWilliam.Kucharski@Sun.COM tftp_close (void)
6778044SWilliam.Kucharski@Sun.COM {
6788044SWilliam.Kucharski@Sun.COM #ifdef TFTP_DEBUG
6798044SWilliam.Kucharski@Sun.COM grub_printf ("tftp_close ()\n");
6808044SWilliam.Kucharski@Sun.COM #endif
6818044SWilliam.Kucharski@Sun.COM
6828044SWilliam.Kucharski@Sun.COM if (use_bios_pxe) {
6838044SWilliam.Kucharski@Sun.COM tftp_close_undi();
6848044SWilliam.Kucharski@Sun.COM return;
6858044SWilliam.Kucharski@Sun.COM }
6868044SWilliam.Kucharski@Sun.COM
6878044SWilliam.Kucharski@Sun.COM buf_read = 0;
6888044SWilliam.Kucharski@Sun.COM buf_fill (1);
6898044SWilliam.Kucharski@Sun.COM }
6908044SWilliam.Kucharski@Sun.COM
6918044SWilliam.Kucharski@Sun.COM /* tftp implementation using BIOS established PXE stack */
6928044SWilliam.Kucharski@Sun.COM
tftp_file_read_undi(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))6938044SWilliam.Kucharski@Sun.COM static int tftp_file_read_undi(const char *name,
6948044SWilliam.Kucharski@Sun.COM int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
6958044SWilliam.Kucharski@Sun.COM {
6968044SWilliam.Kucharski@Sun.COM int rc;
6978044SWilliam.Kucharski@Sun.COM uint16_t len;
6988044SWilliam.Kucharski@Sun.COM
6998044SWilliam.Kucharski@Sun.COM buf = (char *)&nic.packet;
7008044SWilliam.Kucharski@Sun.COM /* open tftp session */
7018044SWilliam.Kucharski@Sun.COM if (eb_pxenv_tftp_open(name, arptable[ARP_SERVER].ipaddr,
7028044SWilliam.Kucharski@Sun.COM arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
7038044SWilliam.Kucharski@Sun.COM return (0);
7048044SWilliam.Kucharski@Sun.COM
7058044SWilliam.Kucharski@Sun.COM /* read blocks and invoke fnc for each block */
7068044SWilliam.Kucharski@Sun.COM for (;;) {
7078044SWilliam.Kucharski@Sun.COM rc = eb_pxenv_tftp_read(buf, &len);
7088044SWilliam.Kucharski@Sun.COM if (rc == 0)
7098044SWilliam.Kucharski@Sun.COM break;
7108044SWilliam.Kucharski@Sun.COM rc = fnc(buf, ++block, len, len < packetsize);
7118044SWilliam.Kucharski@Sun.COM if (rc <= 0 || len < packetsize)
7128044SWilliam.Kucharski@Sun.COM break;
7138044SWilliam.Kucharski@Sun.COM }
7148044SWilliam.Kucharski@Sun.COM
7158044SWilliam.Kucharski@Sun.COM (void) eb_pxenv_tftp_close();
7168044SWilliam.Kucharski@Sun.COM return (rc > 0 ? 1 : 0);
7178044SWilliam.Kucharski@Sun.COM }
7188044SWilliam.Kucharski@Sun.COM
7198044SWilliam.Kucharski@Sun.COM /* Fill the buffer by reading the data via the TFTP protocol. */
7208044SWilliam.Kucharski@Sun.COM static int
buf_fill_undi(int abort)7218044SWilliam.Kucharski@Sun.COM buf_fill_undi(int abort)
7228044SWilliam.Kucharski@Sun.COM {
7238044SWilliam.Kucharski@Sun.COM int rc;
7248044SWilliam.Kucharski@Sun.COM uint8_t *tmpbuf;
7258044SWilliam.Kucharski@Sun.COM
7268044SWilliam.Kucharski@Sun.COM while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN)) {
7278044SWilliam.Kucharski@Sun.COM poll_interruptions();
7288044SWilliam.Kucharski@Sun.COM if (user_abort)
7298044SWilliam.Kucharski@Sun.COM return 0;
7308044SWilliam.Kucharski@Sun.COM if (abort) {
7318044SWilliam.Kucharski@Sun.COM buf_eof = 1;
7328044SWilliam.Kucharski@Sun.COM break;
7338044SWilliam.Kucharski@Sun.COM }
7348044SWilliam.Kucharski@Sun.COM
7358044SWilliam.Kucharski@Sun.COM if (eb_pxenv_tftp_read(buf + buf_read, &len) == 0)
7368044SWilliam.Kucharski@Sun.COM return (0);
7378044SWilliam.Kucharski@Sun.COM
7388044SWilliam.Kucharski@Sun.COM buf_read += len;
7398044SWilliam.Kucharski@Sun.COM
7408044SWilliam.Kucharski@Sun.COM /* End of data. */
7418044SWilliam.Kucharski@Sun.COM if (len < packetsize)
7428044SWilliam.Kucharski@Sun.COM buf_eof = 1;
7438044SWilliam.Kucharski@Sun.COM }
7448044SWilliam.Kucharski@Sun.COM return 1;
7458044SWilliam.Kucharski@Sun.COM }
7468044SWilliam.Kucharski@Sun.COM
7478044SWilliam.Kucharski@Sun.COM static void
tftp_reopen_undi(void)7488044SWilliam.Kucharski@Sun.COM tftp_reopen_undi(void)
7498044SWilliam.Kucharski@Sun.COM {
7508044SWilliam.Kucharski@Sun.COM tftp_close();
7518044SWilliam.Kucharski@Sun.COM (void) eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
7528044SWilliam.Kucharski@Sun.COM arptable[ARP_GATEWAY].ipaddr, &packetsize);
7538044SWilliam.Kucharski@Sun.COM
7548044SWilliam.Kucharski@Sun.COM buf_eof = 0;
7558044SWilliam.Kucharski@Sun.COM buf_read = 0;
7568044SWilliam.Kucharski@Sun.COM saved_filepos = 0;
7578044SWilliam.Kucharski@Sun.COM }
7588044SWilliam.Kucharski@Sun.COM
7598044SWilliam.Kucharski@Sun.COM /* Read up to SIZE bytes, returned in ADDR. */
7608044SWilliam.Kucharski@Sun.COM static int
tftp_read_undi(char * addr,int size)7618044SWilliam.Kucharski@Sun.COM tftp_read_undi(char *addr, int size)
7628044SWilliam.Kucharski@Sun.COM {
7638044SWilliam.Kucharski@Sun.COM int ret = 0;
7648044SWilliam.Kucharski@Sun.COM
7658044SWilliam.Kucharski@Sun.COM if (filepos < saved_filepos) {
7668044SWilliam.Kucharski@Sun.COM /* Uggh.. FILEPOS has been moved backwards. reopen the file. */
7678044SWilliam.Kucharski@Sun.COM tftp_reopen_undi();
7688044SWilliam.Kucharski@Sun.COM }
7698044SWilliam.Kucharski@Sun.COM
7708044SWilliam.Kucharski@Sun.COM while (size > 0) {
7718044SWilliam.Kucharski@Sun.COM int amt = buf_read + saved_filepos - filepos;
7728044SWilliam.Kucharski@Sun.COM
7738044SWilliam.Kucharski@Sun.COM /* If the length that can be copied from the buffer is over
7748044SWilliam.Kucharski@Sun.COM the requested size, cut it down. */
7758044SWilliam.Kucharski@Sun.COM if (amt > size)
7768044SWilliam.Kucharski@Sun.COM amt = size;
7778044SWilliam.Kucharski@Sun.COM
7788044SWilliam.Kucharski@Sun.COM if (amt > 0) {
7798044SWilliam.Kucharski@Sun.COM /* Copy the buffer to the supplied memory space. */
7808044SWilliam.Kucharski@Sun.COM grub_memmove (addr, buf + filepos - saved_filepos, amt);
7818044SWilliam.Kucharski@Sun.COM size -= amt;
7828044SWilliam.Kucharski@Sun.COM addr += amt;
7838044SWilliam.Kucharski@Sun.COM filepos += amt;
7848044SWilliam.Kucharski@Sun.COM ret += amt;
7858044SWilliam.Kucharski@Sun.COM
7868044SWilliam.Kucharski@Sun.COM /* If the size of the empty space becomes small,
7878044SWilliam.Kucharski@Sun.COM * move the unused data forwards.
7888044SWilliam.Kucharski@Sun.COM */
7898044SWilliam.Kucharski@Sun.COM if (filepos - saved_filepos > FSYS_BUFLEN / 2) {
7908044SWilliam.Kucharski@Sun.COM grub_memmove (buf, buf + FSYS_BUFLEN / 2,
7918044SWilliam.Kucharski@Sun.COM FSYS_BUFLEN / 2);
7928044SWilliam.Kucharski@Sun.COM buf_read -= FSYS_BUFLEN / 2;
7938044SWilliam.Kucharski@Sun.COM saved_filepos += FSYS_BUFLEN / 2;
7948044SWilliam.Kucharski@Sun.COM }
7958044SWilliam.Kucharski@Sun.COM } else {
7968044SWilliam.Kucharski@Sun.COM /* Skip the whole buffer. */
7978044SWilliam.Kucharski@Sun.COM saved_filepos += buf_read;
7988044SWilliam.Kucharski@Sun.COM buf_read = 0;
7998044SWilliam.Kucharski@Sun.COM }
8008044SWilliam.Kucharski@Sun.COM
8018044SWilliam.Kucharski@Sun.COM /* Read the data. */
8028044SWilliam.Kucharski@Sun.COM if (size > 0 && ! buf_fill (0)) {
8038044SWilliam.Kucharski@Sun.COM errnum = ERR_READ;
8048044SWilliam.Kucharski@Sun.COM return 0;
8058044SWilliam.Kucharski@Sun.COM }
8068044SWilliam.Kucharski@Sun.COM
8078044SWilliam.Kucharski@Sun.COM /* Sanity check. */
8088044SWilliam.Kucharski@Sun.COM if (size > 0 && buf_read == 0) {
8098044SWilliam.Kucharski@Sun.COM errnum = ERR_READ;
8108044SWilliam.Kucharski@Sun.COM return 0;
8118044SWilliam.Kucharski@Sun.COM }
8128044SWilliam.Kucharski@Sun.COM }
8138044SWilliam.Kucharski@Sun.COM
8148044SWilliam.Kucharski@Sun.COM return ret;
8158044SWilliam.Kucharski@Sun.COM }
8168044SWilliam.Kucharski@Sun.COM
8178044SWilliam.Kucharski@Sun.COM static int
tftp_dir_undi(char * dirname)8188044SWilliam.Kucharski@Sun.COM tftp_dir_undi(char *dirname)
8198044SWilliam.Kucharski@Sun.COM {
8208044SWilliam.Kucharski@Sun.COM int rc, ch;
8218044SWilliam.Kucharski@Sun.COM uint16_t len;
8228044SWilliam.Kucharski@Sun.COM
8238044SWilliam.Kucharski@Sun.COM /* In TFTP, there is no way to know what files exist. */
8248044SWilliam.Kucharski@Sun.COM if (print_possibilities)
8258044SWilliam.Kucharski@Sun.COM return 1;
8268044SWilliam.Kucharski@Sun.COM
8278044SWilliam.Kucharski@Sun.COM /* name may be space terminated */
8288044SWilliam.Kucharski@Sun.COM ch = nul_terminate(dirname);
8298044SWilliam.Kucharski@Sun.COM saved_name = (char *)&saved_tp;
8308044SWilliam.Kucharski@Sun.COM sprintf(saved_name, "%s", dirname);
8318044SWilliam.Kucharski@Sun.COM
8328044SWilliam.Kucharski@Sun.COM /* Restore the original dirname */
8338044SWilliam.Kucharski@Sun.COM dirname[grub_strlen (dirname)] = ch;
8348044SWilliam.Kucharski@Sun.COM
8358044SWilliam.Kucharski@Sun.COM /* get the file size; must call before tftp_open */
8368044SWilliam.Kucharski@Sun.COM rc = eb_pxenv_tftp_get_fsize(saved_name, arptable[ARP_SERVER].ipaddr,
8378044SWilliam.Kucharski@Sun.COM arptable[ARP_GATEWAY].ipaddr, &filemax);
8388044SWilliam.Kucharski@Sun.COM
8398044SWilliam.Kucharski@Sun.COM /* open tftp session */
8408044SWilliam.Kucharski@Sun.COM if (eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
8418044SWilliam.Kucharski@Sun.COM arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
8428044SWilliam.Kucharski@Sun.COM return (0);
8438044SWilliam.Kucharski@Sun.COM
8448044SWilliam.Kucharski@Sun.COM buf = (char *) FSYS_BUF;
8458044SWilliam.Kucharski@Sun.COM buf_eof = 0;
8468044SWilliam.Kucharski@Sun.COM buf_read = 0;
8478044SWilliam.Kucharski@Sun.COM saved_filepos = 0;
8488044SWilliam.Kucharski@Sun.COM
8498044SWilliam.Kucharski@Sun.COM if (rc == 0) {
8508044SWilliam.Kucharski@Sun.COM /* Read the entire file to get filemax */
8518044SWilliam.Kucharski@Sun.COM filemax = 0;
8528044SWilliam.Kucharski@Sun.COM do {
8538044SWilliam.Kucharski@Sun.COM /* Add the length of the downloaded data. */
8548044SWilliam.Kucharski@Sun.COM filemax += buf_read;
8558044SWilliam.Kucharski@Sun.COM buf_read = 0;
8568044SWilliam.Kucharski@Sun.COM if (! buf_fill (0)) {
8578044SWilliam.Kucharski@Sun.COM errnum = ERR_READ;
8588044SWilliam.Kucharski@Sun.COM return 0;
8598044SWilliam.Kucharski@Sun.COM }
8608044SWilliam.Kucharski@Sun.COM } while (! buf_eof);
8618044SWilliam.Kucharski@Sun.COM
8628044SWilliam.Kucharski@Sun.COM /* Maybe a few amounts of data remains. */
8638044SWilliam.Kucharski@Sun.COM filemax += buf_read;
8648044SWilliam.Kucharski@Sun.COM
8658044SWilliam.Kucharski@Sun.COM tftp_reopen_undi(); /* reopen file to read from beginning */
8668044SWilliam.Kucharski@Sun.COM }
8678044SWilliam.Kucharski@Sun.COM
8688044SWilliam.Kucharski@Sun.COM return (1);
8698044SWilliam.Kucharski@Sun.COM }
8708044SWilliam.Kucharski@Sun.COM
8718044SWilliam.Kucharski@Sun.COM static void
tftp_close_undi(void)8728044SWilliam.Kucharski@Sun.COM tftp_close_undi(void)
8738044SWilliam.Kucharski@Sun.COM {
8748044SWilliam.Kucharski@Sun.COM buf_read = 0;
8758044SWilliam.Kucharski@Sun.COM buf_fill (1);
8768044SWilliam.Kucharski@Sun.COM (void) eb_pxenv_tftp_close();
8778044SWilliam.Kucharski@Sun.COM }
878