/* $NetBSD: subr_tftproot.c,v 1.26 2024/07/05 04:31:53 rin Exp $ */
/*-
* Copyright (c) 2007 Emmanuel Dreyfus, all rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Emmanuel Dreyfus
* 4. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Download the root RAMdisk through TFTP at root mount time
*/
if (rootspec != NULL) {
int s = pserialize_read_enter();
IFNET_READER_FOREACH(ifp)
if (strcmp(rootspec, ifp->if_xname) == 0)
break;
pserialize_read_exit(s);
}
if ((ifp == NULL) &&
(bootdv != NULL && device_class(bootdv) == DV_IFNET)) {
int s = pserialize_read_enter();
IFNET_READER_FOREACH(ifp)
if (strcmp(device_xname(bootdv), ifp->if_xname) == 0)
break;
pserialize_read_exit(s);
}
if (ifp == NULL) {
DPRINTF(("%s():%d ifp is NULL\n", __func__, __LINE__));
goto out;
}
/*
* Perform the file transfer
*/
printf("tftproot: download %s:%s ",
inet_ntoa(sin.sin_addr), trh->trh_nd->nd_bootfile);
do {
/*
* Show progress for every 200 blocks (100kB)
*/
#ifndef TFTPROOT_PROGRESS
#define TFTPROOT_PROGRESS 200
#endif
if ((trh->trh_block % TFTPROOT_PROGRESS) == 0)
twiddle();
/*
* Send the packet and receive the answer.
* We get the sender address here, which should be
* the same server with a different port
*/
if ((error = nfs_boot_sendrecv(so, &sin, NULL, m_outbuf,
tftproot_recv, NULL, &m_serv, trh, l)) != 0) {
DPRINTF(("%s():%d sendrecv failed %d\n",
__func__, __LINE__, error));
goto out;
}
/*
* Accommodate the packet length for acks.
* This is really needed only on first pass
*/
m_outbuf->m_len = hdrlen;
m_outbuf->m_pkthdr.len = hdrlen;
tftp->th_opcode = htons((short)ACK);
tftp->th_block = htons(trh->trh_block);
/*
* Check for termination
*/
if (trh->trh_flags & TRH_FINISHED)
break;
trh->trh_block++;
} while (1/* CONSTCOND */);
printf("\n");
/*
* Ack the last block. Ignore errors, as we already have the whole
* file.
*/
if ((error = (*so->so_send)(so, mtod(m_serv, struct sockaddr *), NULL,
m_outbuf, NULL, 0, l)) != 0) {
DPRINTF(("%s():%d tftproot: sosend returned %d\n",
__func__, __LINE__, error));
}
/* Freed by the protocol */
m_outbuf = NULL;
/*
* And use it as the root ramdisk.
*/
DPRINTF(("%s():%d RAMdisk loaded: %ld@%p\n",
__func__, __LINE__, trh->trh_len, trh->trh_base));
md_root_setconf(trh->trh_base, trh->trh_len);
/*
* Check for short packet
*/
if (m->m_pkthdr.len < hdrlen) {
DPRINTF(("%s():%d short reply (%d bytes)\n",
__func__, __LINE__, m->m_pkthdr.len));
return -1;
}
/*
* Check for packet too large for being a TFTP packet
*/
if (m->m_pkthdr.len > hdrlen + SEGSIZE) {
DPRINTF(("%s():%d packet too big (%d bytes)\n",
__func__, __LINE__, m->m_pkthdr.len));
return -1;
}
/*
* Check for last packet, which does not fill the whole space
*/
if (m->m_pkthdr.len < hdrlen + SEGSIZE) {
DPRINTF(("%s():%d last chunk (%d bytes)\n",
__func__, __LINE__, m->m_pkthdr.len));
trh->trh_flags |= TRH_FINISHED;
}