11b8adde7SWilliam Kucharski /*
21b8adde7SWilliam Kucharski * GRUB -- GRand Unified Bootloader
31b8adde7SWilliam Kucharski * Copyright (C) 2000,2001,2002,2004 Free Software Foundation, Inc.
41b8adde7SWilliam Kucharski *
51b8adde7SWilliam Kucharski * This program is free software; you can redistribute it and/or modify
61b8adde7SWilliam Kucharski * it under the terms of the GNU General Public License as published by
71b8adde7SWilliam Kucharski * the Free Software Foundation; either version 2 of the License, or
81b8adde7SWilliam Kucharski * (at your option) any later version.
91b8adde7SWilliam Kucharski *
101b8adde7SWilliam Kucharski * This program is distributed in the hope that it will be useful,
111b8adde7SWilliam Kucharski * but WITHOUT ANY WARRANTY; without even the implied warranty of
121b8adde7SWilliam Kucharski * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
131b8adde7SWilliam Kucharski * GNU General Public License for more details.
141b8adde7SWilliam Kucharski *
151b8adde7SWilliam Kucharski * You should have received a copy of the GNU General Public License
161b8adde7SWilliam Kucharski * along with this program; if not, write to the Free Software
171b8adde7SWilliam Kucharski * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
181b8adde7SWilliam Kucharski */
191b8adde7SWilliam Kucharski
201b8adde7SWilliam Kucharski /* Based on "src/main.c" in etherboot-4.5.8. */
211b8adde7SWilliam Kucharski /**************************************************************************
221b8adde7SWilliam Kucharski ETHERBOOT - BOOTP/TFTP Bootstrap Program
231b8adde7SWilliam Kucharski
241b8adde7SWilliam Kucharski Author: Martin Renters
251b8adde7SWilliam Kucharski Date: Dec/93
261b8adde7SWilliam Kucharski
271b8adde7SWilliam Kucharski **************************************************************************/
281b8adde7SWilliam Kucharski
291b8adde7SWilliam Kucharski /* #define TFTP_DEBUG 1 */
301b8adde7SWilliam Kucharski
311b8adde7SWilliam Kucharski #include <filesys.h>
321b8adde7SWilliam Kucharski #include <shared.h>
331b8adde7SWilliam Kucharski
341b8adde7SWilliam Kucharski #include "grub.h"
351b8adde7SWilliam Kucharski #include "tftp.h"
361b8adde7SWilliam Kucharski #include "nic.h"
371b8adde7SWilliam Kucharski
381b8adde7SWilliam Kucharski static int tftp_file_read_undi(const char *name,
391b8adde7SWilliam Kucharski int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
401b8adde7SWilliam Kucharski static int tftp_read_undi(char *addr, int size);
411b8adde7SWilliam Kucharski static int tftp_dir_undi(char *dirname);
421b8adde7SWilliam Kucharski static void tftp_close_undi(void);
431b8adde7SWilliam Kucharski static int buf_fill_undi(int abort);
441b8adde7SWilliam Kucharski
451b8adde7SWilliam Kucharski extern int use_bios_pxe;
461b8adde7SWilliam Kucharski
471b8adde7SWilliam Kucharski static int retry;
481b8adde7SWilliam Kucharski static unsigned short iport = 2000;
491b8adde7SWilliam Kucharski static unsigned short oport = 0;
501b8adde7SWilliam Kucharski static unsigned short block, prevblock;
511b8adde7SWilliam Kucharski static int bcounter;
521b8adde7SWilliam Kucharski static struct tftp_t tp, saved_tp;
531b8adde7SWilliam Kucharski static int packetsize;
541b8adde7SWilliam Kucharski static int buf_eof, buf_read;
551b8adde7SWilliam Kucharski static int saved_filepos;
561b8adde7SWilliam Kucharski static unsigned short len, saved_len;
571b8adde7SWilliam Kucharski static char *buf, *saved_name;
581b8adde7SWilliam Kucharski
591b8adde7SWilliam Kucharski /**
601b8adde7SWilliam Kucharski * tftp_read
611b8adde7SWilliam Kucharski *
621b8adde7SWilliam Kucharski * Read file with _name_, data handled by _fnc_. In fact, grub never
631b8adde7SWilliam Kucharski * use it, we just use it to read dhcp config file.
641b8adde7SWilliam Kucharski */
await_tftp(int ival,void * ptr __unused,unsigned short ptype __unused,struct iphdr * ip,struct udphdr * udp)651b8adde7SWilliam Kucharski static int await_tftp(int ival, void *ptr __unused,
661b8adde7SWilliam Kucharski unsigned short ptype __unused, struct iphdr *ip,
671b8adde7SWilliam Kucharski struct udphdr *udp)
681b8adde7SWilliam Kucharski {
691b8adde7SWilliam Kucharski static int tftp_count = 0;
701b8adde7SWilliam Kucharski
711b8adde7SWilliam Kucharski if (!udp) {
721b8adde7SWilliam Kucharski return 0;
731b8adde7SWilliam Kucharski }
741b8adde7SWilliam Kucharski if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
751b8adde7SWilliam Kucharski return 0;
761b8adde7SWilliam Kucharski if (ntohs(udp->dest) != ival)
771b8adde7SWilliam Kucharski return 0;
781b8adde7SWilliam Kucharski tftp_count++; /* show progress */
791b8adde7SWilliam Kucharski if ((tftp_count % 1000) == 0)
801b8adde7SWilliam Kucharski printf(".");
811b8adde7SWilliam Kucharski return 1;
821b8adde7SWilliam Kucharski }
831b8adde7SWilliam Kucharski
tftp_file_read(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))841b8adde7SWilliam Kucharski int tftp_file_read(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
851b8adde7SWilliam Kucharski {
861b8adde7SWilliam Kucharski struct tftpreq_t tp;
871b8adde7SWilliam Kucharski struct tftp_t *tr;
881b8adde7SWilliam Kucharski int rc;
891b8adde7SWilliam Kucharski
901b8adde7SWilliam Kucharski if (use_bios_pxe)
911b8adde7SWilliam Kucharski return (tftp_file_read_undi(name, fnc));
921b8adde7SWilliam Kucharski
931b8adde7SWilliam Kucharski retry = 0;
941b8adde7SWilliam Kucharski block = 0;
951b8adde7SWilliam Kucharski prevblock = 0;
961b8adde7SWilliam Kucharski bcounter = 0;
971b8adde7SWilliam Kucharski
981b8adde7SWilliam Kucharski
991b8adde7SWilliam Kucharski rx_qdrain();
1001b8adde7SWilliam Kucharski
1011b8adde7SWilliam Kucharski tp.opcode = htons(TFTP_RRQ);
1021b8adde7SWilliam Kucharski /* Warning: the following assumes the layout of bootp_t.
1031b8adde7SWilliam Kucharski But that's fixed by the IP, UDP and BOOTP specs. */
1041b8adde7SWilliam Kucharski len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
1051b8adde7SWilliam Kucharski sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
1061b8adde7SWilliam Kucharski name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
1071b8adde7SWilliam Kucharski if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
1081b8adde7SWilliam Kucharski TFTP_PORT, len, &tp))
1091b8adde7SWilliam Kucharski return (0);
1101b8adde7SWilliam Kucharski for (;;)
1111b8adde7SWilliam Kucharski {
1121b8adde7SWilliam Kucharski long timeout;
1131b8adde7SWilliam Kucharski #ifdef CONGESTED
1141b8adde7SWilliam Kucharski timeout = rfc2131_sleep_interval(block?TFTP_REXMT: TIMEOUT, retry);
1151b8adde7SWilliam Kucharski #else
1161b8adde7SWilliam Kucharski timeout = rfc2131_sleep_interval(TIMEOUT, retry);
1171b8adde7SWilliam Kucharski #endif
1181b8adde7SWilliam Kucharski if (!await_reply(await_tftp, iport, NULL, timeout))
1191b8adde7SWilliam Kucharski {
1201b8adde7SWilliam Kucharski if (!block && retry++ < MAX_TFTP_RETRIES)
1211b8adde7SWilliam Kucharski { /* maybe initial request was lost */
1221b8adde7SWilliam Kucharski if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1231b8adde7SWilliam Kucharski ++iport, TFTP_PORT, len, &tp))
1241b8adde7SWilliam Kucharski return (0);
1251b8adde7SWilliam Kucharski continue;
1261b8adde7SWilliam Kucharski }
1271b8adde7SWilliam Kucharski #ifdef CONGESTED
1281b8adde7SWilliam Kucharski if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
1291b8adde7SWilliam Kucharski { /* we resend our last ack */
1301b8adde7SWilliam Kucharski #ifdef MDEBUG
1311b8adde7SWilliam Kucharski printf("<REXMT>\n");
1321b8adde7SWilliam Kucharski #endif
1331b8adde7SWilliam Kucharski udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1341b8adde7SWilliam Kucharski iport, oport,
1351b8adde7SWilliam Kucharski TFTP_MIN_PACKET, &tp);
1361b8adde7SWilliam Kucharski continue;
1371b8adde7SWilliam Kucharski }
1381b8adde7SWilliam Kucharski #endif
1391b8adde7SWilliam Kucharski break; /* timeout */
1401b8adde7SWilliam Kucharski }
1411b8adde7SWilliam Kucharski tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
1421b8adde7SWilliam Kucharski if (tr->opcode == ntohs(TFTP_ERROR))
1431b8adde7SWilliam Kucharski {
1441b8adde7SWilliam Kucharski printf("TFTP error %d (%s)\n",
1451b8adde7SWilliam Kucharski ntohs(tr->u.err.errcode),
1461b8adde7SWilliam Kucharski tr->u.err.errmsg);
1471b8adde7SWilliam Kucharski break;
1481b8adde7SWilliam Kucharski }
1491b8adde7SWilliam Kucharski
1501b8adde7SWilliam Kucharski if (tr->opcode == ntohs(TFTP_OACK)) {
1511b8adde7SWilliam Kucharski char *p = tr->u.oack.data, *e;
1521b8adde7SWilliam Kucharski
1531b8adde7SWilliam Kucharski if (prevblock) /* shouldn't happen */
1541b8adde7SWilliam Kucharski continue; /* ignore it */
1551b8adde7SWilliam Kucharski len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
1561b8adde7SWilliam Kucharski if (len > TFTP_MAX_PACKET)
1571b8adde7SWilliam Kucharski goto noak;
1581b8adde7SWilliam Kucharski e = p + len;
1591b8adde7SWilliam Kucharski while (*p != '\0' && p < e) {
1601b8adde7SWilliam Kucharski /* if (!strcasecmp("blksize", p)) { */
1611b8adde7SWilliam Kucharski if (!grub_strcmp("blksize", p)) {
1621b8adde7SWilliam Kucharski p += 8;
1631b8adde7SWilliam Kucharski /* if ((packetsize = strtoul(p, &p, 10)) < */
1641b8adde7SWilliam Kucharski if ((packetsize = getdec(&p)) < TFTP_DEFAULTSIZE_PACKET)
1651b8adde7SWilliam Kucharski goto noak;
1661b8adde7SWilliam Kucharski while (p < e && *p) p++;
1671b8adde7SWilliam Kucharski if (p < e)
1681b8adde7SWilliam Kucharski p++;
1691b8adde7SWilliam Kucharski }
1701b8adde7SWilliam Kucharski else {
1711b8adde7SWilliam Kucharski noak:
1721b8adde7SWilliam Kucharski tp.opcode = htons(TFTP_ERROR);
1731b8adde7SWilliam Kucharski tp.u.err.errcode = 8;
1741b8adde7SWilliam Kucharski /*
1751b8adde7SWilliam Kucharski * Warning: the following assumes the layout of bootp_t.
1761b8adde7SWilliam Kucharski * But that's fixed by the IP, UDP and BOOTP specs.
1771b8adde7SWilliam Kucharski */
1781b8adde7SWilliam Kucharski len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) +
1791b8adde7SWilliam Kucharski /*
1801b8adde7SWilliam Kucharski * Normally bad form to omit the format string, but in this case
1811b8adde7SWilliam Kucharski * the string we are copying from is fixed. sprintf is just being
1821b8adde7SWilliam Kucharski * used as a strcpy and strlen.
1831b8adde7SWilliam Kucharski */
1841b8adde7SWilliam Kucharski sprintf((char *)tp.u.err.errmsg,
1851b8adde7SWilliam Kucharski "RFC1782 error") + 1;
1861b8adde7SWilliam Kucharski udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
1871b8adde7SWilliam Kucharski iport, ntohs(tr->udp.src),
1881b8adde7SWilliam Kucharski len, &tp);
1891b8adde7SWilliam Kucharski return (0);
1901b8adde7SWilliam Kucharski }
1911b8adde7SWilliam Kucharski }
1921b8adde7SWilliam Kucharski if (p > e)
1931b8adde7SWilliam Kucharski goto noak;
1941b8adde7SWilliam Kucharski block = tp.u.ack.block = 0; /* this ensures, that */
1951b8adde7SWilliam Kucharski /* the packet does not get */
1961b8adde7SWilliam Kucharski /* processed as data! */
1971b8adde7SWilliam Kucharski }
1981b8adde7SWilliam Kucharski else if (tr->opcode == htons(TFTP_DATA)) {
1991b8adde7SWilliam Kucharski len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
2001b8adde7SWilliam Kucharski if (len > packetsize) /* shouldn't happen */
2011b8adde7SWilliam Kucharski continue; /* ignore it */
2021b8adde7SWilliam Kucharski block = ntohs(tp.u.ack.block = tr->u.data.block); }
2031b8adde7SWilliam Kucharski else {/* neither TFTP_OACK nor TFTP_DATA */
2041b8adde7SWilliam Kucharski break;
2051b8adde7SWilliam Kucharski }
2061b8adde7SWilliam Kucharski
2071b8adde7SWilliam Kucharski if ((block || bcounter) && (block != (unsigned short)(prevblock+1))) {
2081b8adde7SWilliam Kucharski /* Block order should be continuous */
2091b8adde7SWilliam Kucharski tp.u.ack.block = htons(block = prevblock);
2101b8adde7SWilliam Kucharski }
2111b8adde7SWilliam Kucharski tp.opcode = htons(TFTP_ACK);
2121b8adde7SWilliam Kucharski oport = ntohs(tr->udp.src);
2131b8adde7SWilliam Kucharski udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
2141b8adde7SWilliam Kucharski oport, TFTP_MIN_PACKET, &tp); /* ack */
2151b8adde7SWilliam Kucharski if ((unsigned short)(block-prevblock) != 1) {
2161b8adde7SWilliam Kucharski /* Retransmission or OACK, don't process via callback
2171b8adde7SWilliam Kucharski * and don't change the value of prevblock. */
2181b8adde7SWilliam Kucharski continue;
2191b8adde7SWilliam Kucharski }
2201b8adde7SWilliam Kucharski prevblock = block;
2211b8adde7SWilliam Kucharski retry = 0; /* It's the right place to zero the timer? */
2221b8adde7SWilliam Kucharski if ((rc = fnc(tr->u.data.download,
2231b8adde7SWilliam Kucharski ++bcounter, len, len < packetsize)) <= 0)
2241b8adde7SWilliam Kucharski return(rc);
2251b8adde7SWilliam Kucharski if (len < packetsize) { /* End of data --- fnc should not have returned */
2261b8adde7SWilliam Kucharski printf("tftp download complete, but\n");
2271b8adde7SWilliam Kucharski return (1);
2281b8adde7SWilliam Kucharski }
2291b8adde7SWilliam Kucharski }
2301b8adde7SWilliam Kucharski return (0);
2311b8adde7SWilliam Kucharski }
2321b8adde7SWilliam Kucharski
2331b8adde7SWilliam Kucharski /* Fill the buffer by receiving the data via the TFTP protocol. */
2341b8adde7SWilliam Kucharski static int
buf_fill(int abort)2351b8adde7SWilliam Kucharski buf_fill (int abort)
2361b8adde7SWilliam Kucharski {
2371b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
2381b8adde7SWilliam Kucharski grub_printf ("buf_fill (%d)\n", abort);
2391b8adde7SWilliam Kucharski #endif
2401b8adde7SWilliam Kucharski
2411b8adde7SWilliam Kucharski if (use_bios_pxe)
2421b8adde7SWilliam Kucharski return (buf_fill_undi(abort));
2431b8adde7SWilliam Kucharski
2441b8adde7SWilliam Kucharski while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
2451b8adde7SWilliam Kucharski {
2461b8adde7SWilliam Kucharski struct tftp_t *tr;
2471b8adde7SWilliam Kucharski long timeout;
2481b8adde7SWilliam Kucharski
2491b8adde7SWilliam Kucharski #ifdef CONGESTED
2501b8adde7SWilliam Kucharski timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
2511b8adde7SWilliam Kucharski #else
2521b8adde7SWilliam Kucharski timeout = rfc2131_sleep_interval (TIMEOUT, retry);
2531b8adde7SWilliam Kucharski #endif
2541b8adde7SWilliam Kucharski
2551b8adde7SWilliam Kucharski if (! await_reply (await_tftp, iport, NULL, timeout))
2561b8adde7SWilliam Kucharski {
2571b8adde7SWilliam Kucharski if (user_abort)
2581b8adde7SWilliam Kucharski return 0;
2591b8adde7SWilliam Kucharski
2601b8adde7SWilliam Kucharski if (! block && retry++ < MAX_TFTP_RETRIES)
2611b8adde7SWilliam Kucharski {
2621b8adde7SWilliam Kucharski /* Maybe initial request was lost. */
2631b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
2641b8adde7SWilliam Kucharski grub_printf ("Maybe initial request was lost.\n");
2651b8adde7SWilliam Kucharski #endif
2661b8adde7SWilliam Kucharski if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2671b8adde7SWilliam Kucharski ++iport, TFTP_PORT, len, &tp))
2681b8adde7SWilliam Kucharski return 0;
2691b8adde7SWilliam Kucharski
2701b8adde7SWilliam Kucharski continue;
2711b8adde7SWilliam Kucharski }
2721b8adde7SWilliam Kucharski
2731b8adde7SWilliam Kucharski #ifdef CONGESTED
2741b8adde7SWilliam Kucharski if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
2751b8adde7SWilliam Kucharski {
2761b8adde7SWilliam Kucharski /* We resend our last ack. */
2771b8adde7SWilliam Kucharski # ifdef TFTP_DEBUG
2781b8adde7SWilliam Kucharski grub_printf ("<REXMT>\n");
2791b8adde7SWilliam Kucharski # endif
2801b8adde7SWilliam Kucharski udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
2811b8adde7SWilliam Kucharski iport, oport,
2821b8adde7SWilliam Kucharski TFTP_MIN_PACKET, &tp);
2831b8adde7SWilliam Kucharski continue;
2841b8adde7SWilliam Kucharski }
2851b8adde7SWilliam Kucharski #endif
2861b8adde7SWilliam Kucharski /* Timeout. */
2871b8adde7SWilliam Kucharski return 0;
2881b8adde7SWilliam Kucharski }
2891b8adde7SWilliam Kucharski
2901b8adde7SWilliam Kucharski tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
2911b8adde7SWilliam Kucharski if (tr->opcode == ntohs (TFTP_ERROR))
2921b8adde7SWilliam Kucharski {
2931b8adde7SWilliam Kucharski grub_printf ("TFTP error %d (%s)\n",
2941b8adde7SWilliam Kucharski ntohs (tr->u.err.errcode),
2951b8adde7SWilliam Kucharski tr->u.err.errmsg);
2961b8adde7SWilliam Kucharski return 0;
2971b8adde7SWilliam Kucharski }
2981b8adde7SWilliam Kucharski
2991b8adde7SWilliam Kucharski if (tr->opcode == ntohs (TFTP_OACK))
3001b8adde7SWilliam Kucharski {
3011b8adde7SWilliam Kucharski char *p = tr->u.oack.data, *e;
3021b8adde7SWilliam Kucharski
3031b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3041b8adde7SWilliam Kucharski grub_printf ("OACK ");
3051b8adde7SWilliam Kucharski #endif
3061b8adde7SWilliam Kucharski /* Shouldn't happen. */
3071b8adde7SWilliam Kucharski if (prevblock)
3081b8adde7SWilliam Kucharski {
3091b8adde7SWilliam Kucharski /* Ignore it. */
3101b8adde7SWilliam Kucharski grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
3111b8adde7SWilliam Kucharski __FILE__, __LINE__, prevblock);
3121b8adde7SWilliam Kucharski continue;
3131b8adde7SWilliam Kucharski }
3141b8adde7SWilliam Kucharski
3151b8adde7SWilliam Kucharski len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
3161b8adde7SWilliam Kucharski if (len > TFTP_MAX_PACKET)
3171b8adde7SWilliam Kucharski goto noak;
3181b8adde7SWilliam Kucharski
3191b8adde7SWilliam Kucharski e = p + len;
3201b8adde7SWilliam Kucharski while (*p != '\000' && p < e)
3211b8adde7SWilliam Kucharski {
3221b8adde7SWilliam Kucharski if (! grub_strcmp ("blksize", p))
3231b8adde7SWilliam Kucharski {
3241b8adde7SWilliam Kucharski p += 8;
3251b8adde7SWilliam Kucharski if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
3261b8adde7SWilliam Kucharski goto noak;
3271b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3281b8adde7SWilliam Kucharski grub_printf ("blksize = %d\n", packetsize);
3291b8adde7SWilliam Kucharski #endif
3301b8adde7SWilliam Kucharski }
3311b8adde7SWilliam Kucharski else if (! grub_strcmp ("tsize", p))
3321b8adde7SWilliam Kucharski {
3331b8adde7SWilliam Kucharski p += 6;
3341b8adde7SWilliam Kucharski if ((filemax = getdec (&p)) < 0)
3351b8adde7SWilliam Kucharski {
3361b8adde7SWilliam Kucharski filemax = -1;
3371b8adde7SWilliam Kucharski goto noak;
3381b8adde7SWilliam Kucharski }
3391b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3401b8adde7SWilliam Kucharski grub_printf ("tsize = %d\n", filemax);
3411b8adde7SWilliam Kucharski #endif
3421b8adde7SWilliam Kucharski }
3431b8adde7SWilliam Kucharski else
3441b8adde7SWilliam Kucharski {
3451b8adde7SWilliam Kucharski noak:
3461b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3471b8adde7SWilliam Kucharski grub_printf ("NOAK\n");
3481b8adde7SWilliam Kucharski #endif
3491b8adde7SWilliam Kucharski tp.opcode = htons (TFTP_ERROR);
3501b8adde7SWilliam Kucharski tp.u.err.errcode = 8;
3511b8adde7SWilliam Kucharski len = (grub_sprintf ((char *) tp.u.err.errmsg,
3521b8adde7SWilliam Kucharski "RFC1782 error")
3531b8adde7SWilliam Kucharski + sizeof (tp.ip) + sizeof (tp.udp)
3541b8adde7SWilliam Kucharski + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
3551b8adde7SWilliam Kucharski + 1);
3561b8adde7SWilliam Kucharski udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
3571b8adde7SWilliam Kucharski iport, ntohs (tr->udp.src),
3581b8adde7SWilliam Kucharski len, &tp);
3591b8adde7SWilliam Kucharski return 0;
3601b8adde7SWilliam Kucharski }
3611b8adde7SWilliam Kucharski
3621b8adde7SWilliam Kucharski while (p < e && *p)
3631b8adde7SWilliam Kucharski p++;
3641b8adde7SWilliam Kucharski
3651b8adde7SWilliam Kucharski if (p < e)
3661b8adde7SWilliam Kucharski p++;
3671b8adde7SWilliam Kucharski }
3681b8adde7SWilliam Kucharski
3691b8adde7SWilliam Kucharski if (p > e)
3701b8adde7SWilliam Kucharski goto noak;
3711b8adde7SWilliam Kucharski
3721b8adde7SWilliam Kucharski /* This ensures that the packet does not get processed as
3731b8adde7SWilliam Kucharski data! */
3741b8adde7SWilliam Kucharski block = tp.u.ack.block = 0;
3751b8adde7SWilliam Kucharski }
3761b8adde7SWilliam Kucharski else if (tr->opcode == ntohs (TFTP_DATA))
3771b8adde7SWilliam Kucharski {
3781b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
3791b8adde7SWilliam Kucharski grub_printf ("DATA ");
3801b8adde7SWilliam Kucharski #endif
3811b8adde7SWilliam Kucharski len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
3821b8adde7SWilliam Kucharski
3831b8adde7SWilliam Kucharski /* Shouldn't happen. */
3841b8adde7SWilliam Kucharski if (len > packetsize)
3851b8adde7SWilliam Kucharski {
3861b8adde7SWilliam Kucharski /* Ignore it. */
3871b8adde7SWilliam Kucharski grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
3881b8adde7SWilliam Kucharski __FILE__, __LINE__, len, packetsize);
3891b8adde7SWilliam Kucharski continue;
3901b8adde7SWilliam Kucharski }
3911b8adde7SWilliam Kucharski
3921b8adde7SWilliam Kucharski block = ntohs (tp.u.ack.block = tr->u.data.block);
3931b8adde7SWilliam Kucharski }
3941b8adde7SWilliam Kucharski else
3951b8adde7SWilliam Kucharski /* Neither TFTP_OACK nor TFTP_DATA. */
3961b8adde7SWilliam Kucharski break;
3971b8adde7SWilliam Kucharski
398*df05b9eeSGuoli Shu if ((block || bcounter) && (block != (unsigned short) (prevblock + 1)))
3991b8adde7SWilliam Kucharski /* Block order should be continuous */
4001b8adde7SWilliam Kucharski tp.u.ack.block = htons (block = prevblock);
4011b8adde7SWilliam Kucharski
4021b8adde7SWilliam Kucharski /* Should be continuous. */
4031b8adde7SWilliam Kucharski tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
4041b8adde7SWilliam Kucharski oport = ntohs (tr->udp.src);
4051b8adde7SWilliam Kucharski
4061b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
4071b8adde7SWilliam Kucharski grub_printf ("ACK\n");
4081b8adde7SWilliam Kucharski #endif
4091b8adde7SWilliam Kucharski /* Ack. */
4101b8adde7SWilliam Kucharski udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
4111b8adde7SWilliam Kucharski oport, TFTP_MIN_PACKET, &tp);
4121b8adde7SWilliam Kucharski
4131b8adde7SWilliam Kucharski if (abort)
4141b8adde7SWilliam Kucharski {
4151b8adde7SWilliam Kucharski buf_eof = 1;
4161b8adde7SWilliam Kucharski break;
4171b8adde7SWilliam Kucharski }
4181b8adde7SWilliam Kucharski
4191b8adde7SWilliam Kucharski /* Retransmission or OACK. */
4201b8adde7SWilliam Kucharski if ((unsigned short) (block - prevblock) != 1)
4211b8adde7SWilliam Kucharski /* Don't process. */
4221b8adde7SWilliam Kucharski continue;
4231b8adde7SWilliam Kucharski
4241b8adde7SWilliam Kucharski prevblock = block;
4251b8adde7SWilliam Kucharski /* Is it the right place to zero the timer? */
4261b8adde7SWilliam Kucharski retry = 0;
4271b8adde7SWilliam Kucharski
4281b8adde7SWilliam Kucharski /* In GRUB, this variable doesn't play any important role at all,
4291b8adde7SWilliam Kucharski but use it for consistency with Etherboot. */
4301b8adde7SWilliam Kucharski bcounter++;
4311b8adde7SWilliam Kucharski
4321b8adde7SWilliam Kucharski /* Copy the downloaded data to the buffer. */
4331b8adde7SWilliam Kucharski grub_memmove (buf + buf_read, tr->u.data.download, len);
4341b8adde7SWilliam Kucharski buf_read += len;
4351b8adde7SWilliam Kucharski
4361b8adde7SWilliam Kucharski /* End of data. */
4371b8adde7SWilliam Kucharski if (len < packetsize)
4381b8adde7SWilliam Kucharski buf_eof = 1;
4391b8adde7SWilliam Kucharski }
4401b8adde7SWilliam Kucharski
4411b8adde7SWilliam Kucharski return 1;
4421b8adde7SWilliam Kucharski }
4431b8adde7SWilliam Kucharski
4441b8adde7SWilliam Kucharski /* Send the RRQ whose length is LEN. */
4451b8adde7SWilliam Kucharski static int
send_rrq(void)4461b8adde7SWilliam Kucharski send_rrq (void)
4471b8adde7SWilliam Kucharski {
4481b8adde7SWilliam Kucharski /* Initialize some variables. */
4491b8adde7SWilliam Kucharski retry = 0;
4501b8adde7SWilliam Kucharski block = 0;
4511b8adde7SWilliam Kucharski prevblock = 0;
4521b8adde7SWilliam Kucharski packetsize = TFTP_DEFAULTSIZE_PACKET;
4531b8adde7SWilliam Kucharski bcounter = 0;
4541b8adde7SWilliam Kucharski
4551b8adde7SWilliam Kucharski buf = (char *) FSYS_BUF;
4561b8adde7SWilliam Kucharski buf_eof = 0;
4571b8adde7SWilliam Kucharski buf_read = 0;
4581b8adde7SWilliam Kucharski saved_filepos = 0;
4591b8adde7SWilliam Kucharski
4601b8adde7SWilliam Kucharski rx_qdrain();
4611b8adde7SWilliam Kucharski
4621b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
4631b8adde7SWilliam Kucharski grub_printf ("send_rrq ()\n");
4641b8adde7SWilliam Kucharski {
4651b8adde7SWilliam Kucharski int i;
4661b8adde7SWilliam Kucharski char *p;
4671b8adde7SWilliam Kucharski
4681b8adde7SWilliam Kucharski for (i = 0, p = (char *) &tp; i < len; i++)
4691b8adde7SWilliam Kucharski if (p[i] >= ' ' && p[i] <= '~')
4701b8adde7SWilliam Kucharski grub_putchar (p[i]);
4711b8adde7SWilliam Kucharski else
4721b8adde7SWilliam Kucharski grub_printf ("\\%x", (unsigned) p[i]);
4731b8adde7SWilliam Kucharski
4741b8adde7SWilliam Kucharski grub_putchar ('\n');
4751b8adde7SWilliam Kucharski }
4761b8adde7SWilliam Kucharski #endif
4771b8adde7SWilliam Kucharski /* Send the packet. */
4781b8adde7SWilliam Kucharski return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
4791b8adde7SWilliam Kucharski TFTP_PORT, len, &tp);
4801b8adde7SWilliam Kucharski }
4811b8adde7SWilliam Kucharski
4821b8adde7SWilliam Kucharski /* Mount the network drive. If the drive is ready, return one, otherwise
4831b8adde7SWilliam Kucharski return zero. */
4841b8adde7SWilliam Kucharski int
tftp_mount(void)4851b8adde7SWilliam Kucharski tftp_mount (void)
4861b8adde7SWilliam Kucharski {
4871b8adde7SWilliam Kucharski /* Check if the current drive is the network drive. */
4881b8adde7SWilliam Kucharski if (current_drive != NETWORK_DRIVE)
4891b8adde7SWilliam Kucharski return 0;
4901b8adde7SWilliam Kucharski
4911b8adde7SWilliam Kucharski /* If the drive is not initialized yet, abort. */
4921b8adde7SWilliam Kucharski if (! network_ready)
4931b8adde7SWilliam Kucharski return 0;
4941b8adde7SWilliam Kucharski
4951b8adde7SWilliam Kucharski return 1;
4961b8adde7SWilliam Kucharski }
4971b8adde7SWilliam Kucharski
4981b8adde7SWilliam Kucharski /* Read up to SIZE bytes, returned in ADDR. */
4991b8adde7SWilliam Kucharski int
tftp_read(char * addr,int size)5001b8adde7SWilliam Kucharski tftp_read (char *addr, int size)
5011b8adde7SWilliam Kucharski {
5021b8adde7SWilliam Kucharski /* How many bytes is read? */
5031b8adde7SWilliam Kucharski int ret = 0;
5041b8adde7SWilliam Kucharski
5051b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
5061b8adde7SWilliam Kucharski grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
5071b8adde7SWilliam Kucharski #endif
5081b8adde7SWilliam Kucharski
5091b8adde7SWilliam Kucharski if (use_bios_pxe)
5101b8adde7SWilliam Kucharski return (tftp_read_undi(addr, size));
5111b8adde7SWilliam Kucharski
5121b8adde7SWilliam Kucharski if (filepos < saved_filepos)
5131b8adde7SWilliam Kucharski {
5141b8adde7SWilliam Kucharski /* Uggh.. FILEPOS has been moved backwards. So reopen the file. */
5151b8adde7SWilliam Kucharski buf_read = 0;
5161b8adde7SWilliam Kucharski buf_fill (1);
5171b8adde7SWilliam Kucharski grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
5181b8adde7SWilliam Kucharski len = saved_len;
5191b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
5201b8adde7SWilliam Kucharski {
5211b8adde7SWilliam Kucharski int i;
5221b8adde7SWilliam Kucharski grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
5231b8adde7SWilliam Kucharski for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
5241b8adde7SWilliam Kucharski {
5251b8adde7SWilliam Kucharski if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
5261b8adde7SWilliam Kucharski grub_putchar (tp.u.rrq[i]);
5271b8adde7SWilliam Kucharski else
5281b8adde7SWilliam Kucharski grub_putchar ('*');
5291b8adde7SWilliam Kucharski }
5301b8adde7SWilliam Kucharski grub_putchar ('\n');
5311b8adde7SWilliam Kucharski }
5321b8adde7SWilliam Kucharski #endif
5331b8adde7SWilliam Kucharski
5341b8adde7SWilliam Kucharski if (! send_rrq ())
5351b8adde7SWilliam Kucharski {
5361b8adde7SWilliam Kucharski errnum = ERR_WRITE;
5371b8adde7SWilliam Kucharski return 0;
5381b8adde7SWilliam Kucharski }
5391b8adde7SWilliam Kucharski }
5401b8adde7SWilliam Kucharski
5411b8adde7SWilliam Kucharski while (size > 0)
5421b8adde7SWilliam Kucharski {
5431b8adde7SWilliam Kucharski int amt = buf_read + saved_filepos - filepos;
5441b8adde7SWilliam Kucharski
5451b8adde7SWilliam Kucharski /* If the length that can be copied from the buffer is over the
5461b8adde7SWilliam Kucharski requested size, cut it down. */
5471b8adde7SWilliam Kucharski if (amt > size)
5481b8adde7SWilliam Kucharski amt = size;
5491b8adde7SWilliam Kucharski
5501b8adde7SWilliam Kucharski if (amt > 0)
5511b8adde7SWilliam Kucharski {
5521b8adde7SWilliam Kucharski /* Copy the buffer to the supplied memory space. */
5531b8adde7SWilliam Kucharski grub_memmove (addr, buf + filepos - saved_filepos, amt);
5541b8adde7SWilliam Kucharski size -= amt;
5551b8adde7SWilliam Kucharski addr += amt;
5561b8adde7SWilliam Kucharski filepos += amt;
5571b8adde7SWilliam Kucharski ret += amt;
5581b8adde7SWilliam Kucharski
5591b8adde7SWilliam Kucharski /* If the size of the empty space becomes small, move the unused
5601b8adde7SWilliam Kucharski data forwards. */
5611b8adde7SWilliam Kucharski if (filepos - saved_filepos > FSYS_BUFLEN / 2)
5621b8adde7SWilliam Kucharski {
5631b8adde7SWilliam Kucharski grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
5641b8adde7SWilliam Kucharski buf_read -= FSYS_BUFLEN / 2;
5651b8adde7SWilliam Kucharski saved_filepos += FSYS_BUFLEN / 2;
5661b8adde7SWilliam Kucharski }
5671b8adde7SWilliam Kucharski }
5681b8adde7SWilliam Kucharski else
5691b8adde7SWilliam Kucharski {
5701b8adde7SWilliam Kucharski /* Skip the whole buffer. */
5711b8adde7SWilliam Kucharski saved_filepos += buf_read;
5721b8adde7SWilliam Kucharski buf_read = 0;
5731b8adde7SWilliam Kucharski }
5741b8adde7SWilliam Kucharski
5751b8adde7SWilliam Kucharski /* Read the data. */
5761b8adde7SWilliam Kucharski if (size > 0 && ! buf_fill (0))
5771b8adde7SWilliam Kucharski {
5781b8adde7SWilliam Kucharski errnum = ERR_READ;
5791b8adde7SWilliam Kucharski return 0;
5801b8adde7SWilliam Kucharski }
5811b8adde7SWilliam Kucharski
5821b8adde7SWilliam Kucharski /* Sanity check. */
5831b8adde7SWilliam Kucharski if (size > 0 && buf_read == 0)
5841b8adde7SWilliam Kucharski {
5851b8adde7SWilliam Kucharski errnum = ERR_READ;
5861b8adde7SWilliam Kucharski return 0;
5871b8adde7SWilliam Kucharski }
5881b8adde7SWilliam Kucharski }
5891b8adde7SWilliam Kucharski
5901b8adde7SWilliam Kucharski return ret;
5911b8adde7SWilliam Kucharski }
5921b8adde7SWilliam Kucharski
5931b8adde7SWilliam Kucharski /* Check if the file DIRNAME really exists. Get the size and save it in
5941b8adde7SWilliam Kucharski FILEMAX. */
5951b8adde7SWilliam Kucharski int
tftp_dir(char * dirname)5961b8adde7SWilliam Kucharski tftp_dir (char *dirname)
5971b8adde7SWilliam Kucharski {
5981b8adde7SWilliam Kucharski int ch;
5991b8adde7SWilliam Kucharski
6001b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
6011b8adde7SWilliam Kucharski grub_printf ("tftp_dir (%s)\n", dirname);
6021b8adde7SWilliam Kucharski #endif
6031b8adde7SWilliam Kucharski
6041b8adde7SWilliam Kucharski if (use_bios_pxe)
6051b8adde7SWilliam Kucharski return (tftp_dir_undi(dirname));
6061b8adde7SWilliam Kucharski
6071b8adde7SWilliam Kucharski /* In TFTP, there is no way to know what files exist. */
6081b8adde7SWilliam Kucharski if (print_possibilities)
6091b8adde7SWilliam Kucharski return 1;
6101b8adde7SWilliam Kucharski
6111b8adde7SWilliam Kucharski /* Don't know the size yet. */
6121b8adde7SWilliam Kucharski filemax = -1;
6131b8adde7SWilliam Kucharski
6141b8adde7SWilliam Kucharski reopen:
6151b8adde7SWilliam Kucharski /* Construct the TFTP request packet. */
6161b8adde7SWilliam Kucharski tp.opcode = htons (TFTP_RRQ);
6171b8adde7SWilliam Kucharski /* Terminate the filename. */
6181b8adde7SWilliam Kucharski ch = nul_terminate (dirname);
6191b8adde7SWilliam Kucharski /* Make the request string (octet, blksize and tsize). */
6201b8adde7SWilliam Kucharski len = (grub_sprintf ((char *) tp.u.rrq,
6211b8adde7SWilliam Kucharski "%s%coctet%cblksize%c%d%ctsize%c0",
6221b8adde7SWilliam Kucharski dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
6231b8adde7SWilliam Kucharski + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
6241b8adde7SWilliam Kucharski /* Restore the original DIRNAME. */
6251b8adde7SWilliam Kucharski dirname[grub_strlen (dirname)] = ch;
6261b8adde7SWilliam Kucharski /* Save the TFTP packet so that we can reopen the file later. */
6271b8adde7SWilliam Kucharski grub_memmove ((char *) &saved_tp, (char *) &tp, len);
6281b8adde7SWilliam Kucharski saved_len = len;
6291b8adde7SWilliam Kucharski if (! send_rrq ())
6301b8adde7SWilliam Kucharski {
6311b8adde7SWilliam Kucharski errnum = ERR_WRITE;
6321b8adde7SWilliam Kucharski return 0;
6331b8adde7SWilliam Kucharski }
6341b8adde7SWilliam Kucharski
6351b8adde7SWilliam Kucharski /* Read the data. */
6361b8adde7SWilliam Kucharski if (! buf_fill (0))
6371b8adde7SWilliam Kucharski {
6381b8adde7SWilliam Kucharski errnum = ERR_FILE_NOT_FOUND;
6391b8adde7SWilliam Kucharski return 0;
6401b8adde7SWilliam Kucharski }
6411b8adde7SWilliam Kucharski
6421b8adde7SWilliam Kucharski if (filemax == -1)
6431b8adde7SWilliam Kucharski {
6441b8adde7SWilliam Kucharski /* The server doesn't support the "tsize" option, so we must read
6451b8adde7SWilliam Kucharski the file twice... */
6461b8adde7SWilliam Kucharski
6471b8adde7SWilliam Kucharski /* Zero the size of the file. */
6481b8adde7SWilliam Kucharski filemax = 0;
6491b8adde7SWilliam Kucharski do
6501b8adde7SWilliam Kucharski {
6511b8adde7SWilliam Kucharski /* Add the length of the downloaded data. */
6521b8adde7SWilliam Kucharski filemax += buf_read;
6531b8adde7SWilliam Kucharski /* Reset the offset. Just discard the contents of the buffer. */
6541b8adde7SWilliam Kucharski buf_read = 0;
6551b8adde7SWilliam Kucharski /* Read the data. */
6561b8adde7SWilliam Kucharski if (! buf_fill (0))
6571b8adde7SWilliam Kucharski {
6581b8adde7SWilliam Kucharski errnum = ERR_READ;
6591b8adde7SWilliam Kucharski return 0;
6601b8adde7SWilliam Kucharski }
6611b8adde7SWilliam Kucharski }
6621b8adde7SWilliam Kucharski while (! buf_eof);
6631b8adde7SWilliam Kucharski
6641b8adde7SWilliam Kucharski /* Maybe a few amounts of data remains. */
6651b8adde7SWilliam Kucharski filemax += buf_read;
6661b8adde7SWilliam Kucharski
6671b8adde7SWilliam Kucharski /* Retry the open instruction. */
6681b8adde7SWilliam Kucharski goto reopen;
6691b8adde7SWilliam Kucharski }
6701b8adde7SWilliam Kucharski
6711b8adde7SWilliam Kucharski return 1;
6721b8adde7SWilliam Kucharski }
6731b8adde7SWilliam Kucharski
6741b8adde7SWilliam Kucharski /* Close the file. */
6751b8adde7SWilliam Kucharski void
tftp_close(void)6761b8adde7SWilliam Kucharski tftp_close (void)
6771b8adde7SWilliam Kucharski {
6781b8adde7SWilliam Kucharski #ifdef TFTP_DEBUG
6791b8adde7SWilliam Kucharski grub_printf ("tftp_close ()\n");
6801b8adde7SWilliam Kucharski #endif
6811b8adde7SWilliam Kucharski
6821b8adde7SWilliam Kucharski if (use_bios_pxe) {
6831b8adde7SWilliam Kucharski tftp_close_undi();
6841b8adde7SWilliam Kucharski return;
6851b8adde7SWilliam Kucharski }
6861b8adde7SWilliam Kucharski
6871b8adde7SWilliam Kucharski buf_read = 0;
6881b8adde7SWilliam Kucharski buf_fill (1);
6891b8adde7SWilliam Kucharski }
6901b8adde7SWilliam Kucharski
6911b8adde7SWilliam Kucharski /* tftp implementation using BIOS established PXE stack */
6921b8adde7SWilliam Kucharski
tftp_file_read_undi(const char * name,int (* fnc)(unsigned char *,unsigned int,unsigned int,int))6931b8adde7SWilliam Kucharski static int tftp_file_read_undi(const char *name,
6941b8adde7SWilliam Kucharski int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
6951b8adde7SWilliam Kucharski {
6961b8adde7SWilliam Kucharski int rc;
6971b8adde7SWilliam Kucharski uint16_t len;
6981b8adde7SWilliam Kucharski
6991b8adde7SWilliam Kucharski buf = (char *)&nic.packet;
7001b8adde7SWilliam Kucharski /* open tftp session */
7011b8adde7SWilliam Kucharski if (eb_pxenv_tftp_open(name, arptable[ARP_SERVER].ipaddr,
7021b8adde7SWilliam Kucharski arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
7031b8adde7SWilliam Kucharski return (0);
7041b8adde7SWilliam Kucharski
7051b8adde7SWilliam Kucharski /* read blocks and invoke fnc for each block */
7061b8adde7SWilliam Kucharski for (;;) {
7071b8adde7SWilliam Kucharski rc = eb_pxenv_tftp_read(buf, &len);
7081b8adde7SWilliam Kucharski if (rc == 0)
7091b8adde7SWilliam Kucharski break;
7101b8adde7SWilliam Kucharski rc = fnc(buf, ++block, len, len < packetsize);
7111b8adde7SWilliam Kucharski if (rc <= 0 || len < packetsize)
7121b8adde7SWilliam Kucharski break;
7131b8adde7SWilliam Kucharski }
7141b8adde7SWilliam Kucharski
7151b8adde7SWilliam Kucharski (void) eb_pxenv_tftp_close();
7161b8adde7SWilliam Kucharski return (rc > 0 ? 1 : 0);
7171b8adde7SWilliam Kucharski }
7181b8adde7SWilliam Kucharski
7191b8adde7SWilliam Kucharski /* Fill the buffer by reading the data via the TFTP protocol. */
7201b8adde7SWilliam Kucharski static int
buf_fill_undi(int abort)7211b8adde7SWilliam Kucharski buf_fill_undi(int abort)
7221b8adde7SWilliam Kucharski {
7231b8adde7SWilliam Kucharski int rc;
7241b8adde7SWilliam Kucharski uint8_t *tmpbuf;
7251b8adde7SWilliam Kucharski
7261b8adde7SWilliam Kucharski while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN)) {
7271b8adde7SWilliam Kucharski poll_interruptions();
7281b8adde7SWilliam Kucharski if (user_abort)
7291b8adde7SWilliam Kucharski return 0;
7301b8adde7SWilliam Kucharski if (abort) {
7311b8adde7SWilliam Kucharski buf_eof = 1;
7321b8adde7SWilliam Kucharski break;
7331b8adde7SWilliam Kucharski }
7341b8adde7SWilliam Kucharski
7351b8adde7SWilliam Kucharski if (eb_pxenv_tftp_read(buf + buf_read, &len) == 0)
7361b8adde7SWilliam Kucharski return (0);
7371b8adde7SWilliam Kucharski
7381b8adde7SWilliam Kucharski buf_read += len;
7391b8adde7SWilliam Kucharski
7401b8adde7SWilliam Kucharski /* End of data. */
7411b8adde7SWilliam Kucharski if (len < packetsize)
7421b8adde7SWilliam Kucharski buf_eof = 1;
7431b8adde7SWilliam Kucharski }
7441b8adde7SWilliam Kucharski return 1;
7451b8adde7SWilliam Kucharski }
7461b8adde7SWilliam Kucharski
7471b8adde7SWilliam Kucharski static void
tftp_reopen_undi(void)7481b8adde7SWilliam Kucharski tftp_reopen_undi(void)
7491b8adde7SWilliam Kucharski {
7501b8adde7SWilliam Kucharski tftp_close();
7511b8adde7SWilliam Kucharski (void) eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
7521b8adde7SWilliam Kucharski arptable[ARP_GATEWAY].ipaddr, &packetsize);
7531b8adde7SWilliam Kucharski
7541b8adde7SWilliam Kucharski buf_eof = 0;
7551b8adde7SWilliam Kucharski buf_read = 0;
7561b8adde7SWilliam Kucharski saved_filepos = 0;
7571b8adde7SWilliam Kucharski }
7581b8adde7SWilliam Kucharski
7591b8adde7SWilliam Kucharski /* Read up to SIZE bytes, returned in ADDR. */
7601b8adde7SWilliam Kucharski static int
tftp_read_undi(char * addr,int size)7611b8adde7SWilliam Kucharski tftp_read_undi(char *addr, int size)
7621b8adde7SWilliam Kucharski {
7631b8adde7SWilliam Kucharski int ret = 0;
7641b8adde7SWilliam Kucharski
7651b8adde7SWilliam Kucharski if (filepos < saved_filepos) {
7661b8adde7SWilliam Kucharski /* Uggh.. FILEPOS has been moved backwards. reopen the file. */
7671b8adde7SWilliam Kucharski tftp_reopen_undi();
7681b8adde7SWilliam Kucharski }
7691b8adde7SWilliam Kucharski
7701b8adde7SWilliam Kucharski while (size > 0) {
7711b8adde7SWilliam Kucharski int amt = buf_read + saved_filepos - filepos;
7721b8adde7SWilliam Kucharski
7731b8adde7SWilliam Kucharski /* If the length that can be copied from the buffer is over
7741b8adde7SWilliam Kucharski the requested size, cut it down. */
7751b8adde7SWilliam Kucharski if (amt > size)
7761b8adde7SWilliam Kucharski amt = size;
7771b8adde7SWilliam Kucharski
7781b8adde7SWilliam Kucharski if (amt > 0) {
7791b8adde7SWilliam Kucharski /* Copy the buffer to the supplied memory space. */
7801b8adde7SWilliam Kucharski grub_memmove (addr, buf + filepos - saved_filepos, amt);
7811b8adde7SWilliam Kucharski size -= amt;
7821b8adde7SWilliam Kucharski addr += amt;
7831b8adde7SWilliam Kucharski filepos += amt;
7841b8adde7SWilliam Kucharski ret += amt;
7851b8adde7SWilliam Kucharski
7861b8adde7SWilliam Kucharski /* If the size of the empty space becomes small,
7871b8adde7SWilliam Kucharski * move the unused data forwards.
7881b8adde7SWilliam Kucharski */
7891b8adde7SWilliam Kucharski if (filepos - saved_filepos > FSYS_BUFLEN / 2) {
7901b8adde7SWilliam Kucharski grub_memmove (buf, buf + FSYS_BUFLEN / 2,
7911b8adde7SWilliam Kucharski FSYS_BUFLEN / 2);
7921b8adde7SWilliam Kucharski buf_read -= FSYS_BUFLEN / 2;
7931b8adde7SWilliam Kucharski saved_filepos += FSYS_BUFLEN / 2;
7941b8adde7SWilliam Kucharski }
7951b8adde7SWilliam Kucharski } else {
7961b8adde7SWilliam Kucharski /* Skip the whole buffer. */
7971b8adde7SWilliam Kucharski saved_filepos += buf_read;
7981b8adde7SWilliam Kucharski buf_read = 0;
7991b8adde7SWilliam Kucharski }
8001b8adde7SWilliam Kucharski
8011b8adde7SWilliam Kucharski /* Read the data. */
8021b8adde7SWilliam Kucharski if (size > 0 && ! buf_fill (0)) {
8031b8adde7SWilliam Kucharski errnum = ERR_READ;
8041b8adde7SWilliam Kucharski return 0;
8051b8adde7SWilliam Kucharski }
8061b8adde7SWilliam Kucharski
8071b8adde7SWilliam Kucharski /* Sanity check. */
8081b8adde7SWilliam Kucharski if (size > 0 && buf_read == 0) {
8091b8adde7SWilliam Kucharski errnum = ERR_READ;
8101b8adde7SWilliam Kucharski return 0;
8111b8adde7SWilliam Kucharski }
8121b8adde7SWilliam Kucharski }
8131b8adde7SWilliam Kucharski
8141b8adde7SWilliam Kucharski return ret;
8151b8adde7SWilliam Kucharski }
8161b8adde7SWilliam Kucharski
8171b8adde7SWilliam Kucharski static int
tftp_dir_undi(char * dirname)8181b8adde7SWilliam Kucharski tftp_dir_undi(char *dirname)
8191b8adde7SWilliam Kucharski {
8201b8adde7SWilliam Kucharski int rc, ch;
8211b8adde7SWilliam Kucharski uint16_t len;
8221b8adde7SWilliam Kucharski
8231b8adde7SWilliam Kucharski /* In TFTP, there is no way to know what files exist. */
8241b8adde7SWilliam Kucharski if (print_possibilities)
8251b8adde7SWilliam Kucharski return 1;
8261b8adde7SWilliam Kucharski
8271b8adde7SWilliam Kucharski /* name may be space terminated */
8281b8adde7SWilliam Kucharski ch = nul_terminate(dirname);
8291b8adde7SWilliam Kucharski saved_name = (char *)&saved_tp;
8301b8adde7SWilliam Kucharski sprintf(saved_name, "%s", dirname);
8311b8adde7SWilliam Kucharski
8321b8adde7SWilliam Kucharski /* Restore the original dirname */
8331b8adde7SWilliam Kucharski dirname[grub_strlen (dirname)] = ch;
8341b8adde7SWilliam Kucharski
8351b8adde7SWilliam Kucharski /* get the file size; must call before tftp_open */
8361b8adde7SWilliam Kucharski rc = eb_pxenv_tftp_get_fsize(saved_name, arptable[ARP_SERVER].ipaddr,
8371b8adde7SWilliam Kucharski arptable[ARP_GATEWAY].ipaddr, &filemax);
8381b8adde7SWilliam Kucharski
8391b8adde7SWilliam Kucharski /* open tftp session */
8401b8adde7SWilliam Kucharski if (eb_pxenv_tftp_open(saved_name, arptable[ARP_SERVER].ipaddr,
8411b8adde7SWilliam Kucharski arptable[ARP_GATEWAY].ipaddr, &packetsize) == 0)
8421b8adde7SWilliam Kucharski return (0);
8431b8adde7SWilliam Kucharski
8441b8adde7SWilliam Kucharski buf = (char *) FSYS_BUF;
8451b8adde7SWilliam Kucharski buf_eof = 0;
8461b8adde7SWilliam Kucharski buf_read = 0;
8471b8adde7SWilliam Kucharski saved_filepos = 0;
8481b8adde7SWilliam Kucharski
8491b8adde7SWilliam Kucharski if (rc == 0) {
8501b8adde7SWilliam Kucharski /* Read the entire file to get filemax */
8511b8adde7SWilliam Kucharski filemax = 0;
8521b8adde7SWilliam Kucharski do {
8531b8adde7SWilliam Kucharski /* Add the length of the downloaded data. */
8541b8adde7SWilliam Kucharski filemax += buf_read;
8551b8adde7SWilliam Kucharski buf_read = 0;
8561b8adde7SWilliam Kucharski if (! buf_fill (0)) {
8571b8adde7SWilliam Kucharski errnum = ERR_READ;
8581b8adde7SWilliam Kucharski return 0;
8591b8adde7SWilliam Kucharski }
8601b8adde7SWilliam Kucharski } while (! buf_eof);
8611b8adde7SWilliam Kucharski
8621b8adde7SWilliam Kucharski /* Maybe a few amounts of data remains. */
8631b8adde7SWilliam Kucharski filemax += buf_read;
8641b8adde7SWilliam Kucharski
8651b8adde7SWilliam Kucharski tftp_reopen_undi(); /* reopen file to read from beginning */
8661b8adde7SWilliam Kucharski }
8671b8adde7SWilliam Kucharski
8681b8adde7SWilliam Kucharski return (1);
8691b8adde7SWilliam Kucharski }
8701b8adde7SWilliam Kucharski
8711b8adde7SWilliam Kucharski static void
tftp_close_undi(void)8721b8adde7SWilliam Kucharski tftp_close_undi(void)
8731b8adde7SWilliam Kucharski {
8741b8adde7SWilliam Kucharski buf_read = 0;
8751b8adde7SWilliam Kucharski buf_fill (1);
8761b8adde7SWilliam Kucharski (void) eb_pxenv_tftp_close();
8771b8adde7SWilliam Kucharski }
878