/*
 **************************************************************************
 *
 * Boot-ROM-Code to load an operating system across a TCP/IP network.
 *
 * Module:  boot.c
 * Purpose: Perform the actual booting process
 * Entries: do_boot
 *
 **************************************************************************
 *
 * Copyright (C) 1995-2003 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: boot.c,v 1.6 2003/01/25 23:29:41 gkminix Exp $
 */


#include <general.h>
#include <kernel/net.h>
#include <kernel/arpa.h>
#include <kernel/romlib.h>
#include <pxe/common.h>
#include <pxe/pxegeneral.h>
#include "bootpriv.h"
#include "menu.h"
#include "load.h"


/*
 **************************************************************************
 * 
 * Variables local to this module
 */
static jmp_buf restartenv;		/* setjmp buffer for restart	*/



/*
 **************************************************************************
 * 
 * Print an IP number. Note that the IP address should be in network
 * order.
 */
static void printip(ip)
t_ipaddr ip;
{
  register unsigned char *cp = (unsigned char *)&ip;

  printf("%u.%u.%u.%u", cp[0], cp[1], cp[2], cp[3]);
}



/*
 **************************************************************************
 * 
 * Set parameters from BOOTP record
 */
static int setparams()
{
  register unsigned char *cp;
  register int len;
  unsigned char *sname;
  int snamlen;
  int overload = 0;

  cur_bootp_buf = BOOTP_REPLY;
  memset(&ldparams, 0, sizeof(ldparams));

  /* Get the overloading tag */
  if ((cp = get_vend(VEND_OVERLOAD)) != NULL)
	overload = *(cp + 1);

  /* Determine the IP address of the TFTP server */
  if ((cp = get_vend(VEND_SERVER)) != NULL)
	ldparams.tftp.server = *((t_ipaddr *)(cp + 1));
  else
	ldparams.tftp.server = bootp_bufs[BOOTP_REPLY]->bp_siaddr;
  if (ldparams.tftp.server == n_IP_ANY)
	return(PXENV_STATUS_DHCP_NO_IP_ADDR);

  /* Determine name of server the boot image file will come from */
  if ((cp = get_vend(VEND_TFTPNAME)) != NULL) {
	sname = cp + 1;
	snamlen = *cp;
  } else if (!(overload & VEND_OVR_SNAME)) {
	sname = bootp_bufs[BOOTP_REPLY]->bp_sname;
	snamlen = BOOTP_SNAME_SIZE;
  } else
	sname = NULL;

  /* Determine name of boot image file to load */
  if ((cp = get_vend(VEND_BOOTFILE)) != NULL) {
	len = *cp;
	if (len > sizeof(ldparams.tftp.filename))
		len = sizeof(ldparams.tftp.filename);
	memcpy(ldparams.tftp.filename, cp + 1, len);
  } else if (!(overload & VEND_OVR_FILE))
	memcpy(ldparams.tftp.filename, bootp_bufs[BOOTP_REPLY]->bp_file,
						sizeof(ldparams.tftp.filename));
  if (ldparams.tftp.filename[0] == '\0')
	return(PXENV_STATUS_DHCP_NO_FILE);

  /* Get any router information */
  if ((cp = get_vend(VEND_ROUTER)) != NULL)
	ldparams.tftp.gateway = *((t_ipaddr *)(cp + 1));

  /* Print some of the information we got so far */
  printf("\n\nLocal IP:   "); printip(myipaddr);
  printf("\nServer IP:  "); printip(ldparams.tftp.server);
  if (sname != NULL)
	printf("  (%ls)", sname, snamlen);
  if (ldparams.tftp.gateway != n_IP_ANY) {
	printf("\nGateway IP: ");
	printip(ldparams.tftp.gateway);
  }
  printf("\n");
  return(PXENV_STATUS_SUCCESS);
}



/*
 **************************************************************************
 * 
 * Actually perform the booting process by first calling the BOOTP client,
 * and then loading the operating system using TFTP. This routine gets
 * called from the assembler startup code.
 *
 */
int do_boot()
{
  int ret;

  /* Wait some seconds for the network card to settle down */
  set_timeout(3);
  while (!chk_timeout()) ;

  /* Now start the network booting process */
  if ((ret = bootp()) == PXENV_STATUS_SUCCESS) {
#ifndef NOMENU
	if (domenu() != MENU_ABORT) {
#endif
		while ((ret = setparams()) == PXENV_STATUS_SUCCESS) {
			if ((ret = load(FALSE)) != PXENV_STATUS_SUCCESS)
				break;
			printf("\n\nStarting image...\n");
			setjmp(&restartenv);
			if (!exec_image(ldparams.mode,
					ldparams.exec,
			                ldparams.header,
			                bootp_bufs[BOOTP_REPLY],
			                ldparams.drive)) {
				ret = PXENV_STATUS_SUCCESS;
				break;
			}
		}
#ifndef NOMENU
	}
#endif
  }
  return(ret);
}



/*
 **************************************************************************
 * 
 * Restart TFTP PXE function
 */
int pxe_restart(params)
t_tftp_read_file *params;
{
  register int ret = PXENV_STATUS_FAILURE;

  memset(&ldparams, 0, sizeof(ldparams));
  memcpy(&ldparams.tftp, params, sizeof(ldparams.tftp));
  if ((ret = load(TRUE)) == PXENV_STATUS_SUCCESS)
	/*
	 * We have to use longjump here to jump back to the original
	 * bootrom loader code. Otherwise, the bootrom stack will
	 * overflow with multiple calls of this routine.
	 */
	longjmp(&restartenv, 0);

  return(ret);
}



/*
 **************************************************************************
 * 
 * Return BOOTP/DHCP information
 */
int pxe_get_binl_info(params)
t_gen_get_binl_info *params;
{
  register unsigned char *cp = NULL;
  unsigned int len, limit, ret;

  ret = PXENV_STATUS_FAILURE;
  if (params->packet_type >= GEN_PACKET_DHCP_DISCOVER &&
      params->packet_type <= GEN_PACKET_BINL_REPLY) {
	cur_bootp_buf = params->packet_type - 1;
	cp = (unsigned char *)(bootp_bufs[cur_bootp_buf]);
	limit = bootp_sizes[cur_bootp_buf];
	len = get_vend(VEND_END) - cp;
	if ((params->buffer_offset + params->buffer_segment +
	     params->buffer_size) == 0) {
		params->buffer_size = len;
		params->buffer_offset = (unsigned int)cp;
		params->buffer_segment = getds();
		params->buffer_limit = limit;
		ret = PXENV_STATUS_SUCCESS;
	} else if (params->buffer_size >= len) {
		fmemcpy(params->buffer_offset, params->buffer_segment, cp, len);
		params->buffer_size = len;
		params->buffer_limit = limit;
		ret = PXENV_STATUS_SUCCESS;
	}
  }
  return(ret);
}



syntax highlighted by Code2HTML, v. 0.9.1