/*	$OpenBSD: boot.c,v 1.25 2014/02/19 22:13:53 miod Exp $ */
/*	$NetBSD: boot.c,v 1.18 2002/05/31 15:58:26 ragge Exp $ */
/*-
 * Copyright (c) 1982, 1986 The Regents of the University of California.
 * 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. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
 *
 *	@(#)boot.c	7.15 (Berkeley) 5/4/91
 */

#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#define _KERNEL
#include <sys/fcntl.h>
#undef _KERNEL

#include <lib/libkern/libkern.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/loadfile.h>

#include "machine/rpb.h"
#include "machine/sid.h"

#include "vaxstand.h"

/*
 * Boot program... arguments passed in r10 and r11 determine
 * whether boot stops to ask for system name and which device
 * boot comes from.
 */

char line[100];
int	bootdev, debug;
extern	unsigned opendev;

char   rnddata[BOOTRANDOM_MAX];

void	usage(char *), boot(char *), halt(char *);
void	Xmain(void);
void	autoconf(void);
time_t	getsecs(void);
int	loadrandom(const char *, char *, size_t);
int	setjmp(int *);
int	testkey(void);

const struct vals {
	char	*namn;
	void	(*func)(char *);
	char	*info;
} val[] = {
	{"?", usage, "Show this help menu"},
	{"help", usage, "Same as '?'"},
	{"boot", boot, "Load and execute file"},
	{"halt", halt, "Halts the system"},
	{0, 0},
};

int jbuf[10];
int sluttid, senast, skip, askname;
int mcheck_silent;
struct rpb bootrpb;

void
Xmain(void)
{
	int io;
	int j, nu;
	char transition = '\010';
	u_long marks[MARK_MAX];

	io = 0;
	skip = 1;
	autoconf();

	/*
	 * Some VAXstation 4000 PROMs slowly erase the whole screen with \010
	 * if running with glass console - at least VS4000/60 and VS4000/VLC;
	 * this is probably the LCG PROM at fault. Use a different transition
	 * pattern, it's not as nice but it does not take 3(!) seconds to
	 * display...
	 */
	if (((vax_boardtype == VAX_BTYP_46 &&
	      (vax_siedata & 0xff) == VAX_VTYP_46) ||
	     (vax_boardtype == VAX_BTYP_48 &&
	      vax_cpustype == VAX_STYP_48)) &&
	    (vax_confdata & 0x100) == 0)
		transition = ' ';

	askname = bootrpb.rpb_bootr5 & RB_ASKNAME;
	printf("\n\r>> OpenBSD/vax boot [%s] <<\n", "1.18");
	printf(">> Press enter to autoboot now, or any other key to abort:  ");
	sluttid = getsecs() + 5;
	senast = 0;
	skip = 0;
	setjmp(jbuf);
	for (;;) {
		nu = sluttid - getsecs();
		if (senast != nu)
			printf("%c%d", transition, nu);
		if (nu <= 0)
			break;
		senast = nu;
		if ((j = (testkey() & 0177))) {
			skip = 1;
			if (j != 10 && j != 13) {
				printf("\nPress '?' for help");
				askname = 1;
			}
			break;
		}
	}
	skip = 1;
	printf("\n");

	if (setjmp(jbuf))
		askname = 1;

	/* First try to autoboot */
	if (askname == 0) {
		int err;

		errno = 0;
		printf("> boot bsd\n");
		boot(NULL);
	}

	/* If any key pressed, or autoboot failed, go to conversational boot */
	for (;;) {
		const struct vals *v = &val[0];
		char *c, *d;

		printf("> ");
		gets(line);

		c = line;
		while (*c == ' ')
			c++;

		if (c[0] == 0)
			continue;

		if ((d = strchr(c, ' ')))
			*d++ = 0;

		while (v->namn) {
			if (strcmp(v->namn, c) == 0)
				break;
			v++;
		}
		if (v->namn)
			(*v->func)(d);
		else
			printf("Unknown command: %s\n", c);
	}
}

void
halt(char *hej)
{
	asm("halt");
}

void
boot(char *arg)
{
	char *fn = "bsd";
	int howto, err;
	u_long marks[MARK_MAX];
	static int rnd_loaded = 0;

	if (arg) {
		while (*arg == ' ')
			arg++;

		if (*arg != '-') {
			fn = arg;
			if ((arg = strchr(arg, ' '))) {
				*arg++ = 0;
				while (*arg == ' ')
					arg++;
			} else
				goto load;
		}
		if (*arg != '-') {
fail:			printf("usage: boot [filename] [-acsd]\n");
			return;
		}

		howto = 0;

		while (*++arg) {
			if (*arg == 'a')
				howto |= RB_ASKNAME;
			else if (*arg == 'c')
				howto |= RB_CONFIG;
			else if (*arg == 'd')
				howto |= RB_KDB;
			else if (*arg == 's')
				howto |= RB_SINGLE;
			else
				goto fail;
		}
		bootrpb.rpb_bootr5 = howto;
	}
load:  
	/*
	 * Attempt to load /etc/random.seed if loading from a disk.
	 */
	switch (bootrpb.devtyp) {
	default:
		break;
	case BDEV_UDA:
	case BDEV_RD:
	case BDEV_SD:
	case BDEV_SDN:
	case BDEV_SDS:
		if (rnd_loaded == 0)
			rnd_loaded = loadrandom(BOOTRANDOM, rnddata,
			    sizeof(rnddata));
		break;
	}

	marks[MARK_START] = 0;
	err = loadfile(fn, marks, LOAD_KERNEL|COUNT_KERNEL);
	if (err == 0) {
		machdep_start((char *)marks[MARK_ENTRY],
				      marks[MARK_NSYM],
			      (void *)marks[MARK_START],
			      (void *)marks[MARK_SYM],
			      (void *)marks[MARK_END]);
	}
	printf("%s: boot failed: %s\n", fn, strerror(errno));
}

void
usage(char *hej)
{
	const struct vals *v = &val[0];
	int i;

	printf("Commands:\n");
	while (v->namn) {
		printf("%s ", v->namn);
		for (i = 1 + strlen(v->namn); (i & 7) != 0; i++)
			printf(" ");
		printf("%s\n", v->info);
		v++;
	}
}

int
loadrandom(const char *name, char *buf, size_t buflen)
{
	struct stat sb;
	int fd;
	int rc = 0;

	fd = open(name, O_RDONLY);
	if (fd == -1) {
		if (errno != EPERM)
			printf("cannot open %s: %s\n", name, strerror(errno));
		return 0;
	}
	if (fstat(fd, &sb) == -1 || sb.st_uid != 0 || !S_ISREG(sb.st_mode) ||
	    (sb.st_mode & (S_IWOTH|S_IROTH)))
		goto fail;
	(void) read(fd, buf, buflen);
	rc = 1;
fail:
	close(fd);
	return rc;
}
