Subject: Adding a prompt to bootloader to specify kernel name
To: None <port-cobalt@NetBSD.org>
From: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
List: port-cobalt
Date: 04/03/2004 20:54:03
Today I modify bootloader in cobalt/stand/boot to add a prompt
to specify a kernel filename to be loaded. This makes it easier
to recover system when newer kernel does not boot.

I've put boot.gz binary at:
http://www.ceres.dti.ne.jp/~tsutsui/netbsd/boot-cobalt-20040403.gz
and source diff is attached.

Some of these code might need enhancement and bootinfo structures
in a kernel should also be reorganized, but I think still it useful
for now.

Comments?
---
Izumi Tsutsui
tsutsui@ceres.dti.ne.jp

Index: stand/boot/Makefile
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/Makefile,v
retrieving revision 1.6
diff -u -r1.6 Makefile
--- stand/boot/Makefile	27 Jan 2004 21:03:18 -0000	1.6
+++ stand/boot/Makefile	3 Apr 2004 00:05:36 -0000
@@ -68,7 +68,7 @@
 PROG=		boot
 # common sources
 SRCS+=		start.S boot.c devopen.c conf.c clock.c bootinfo.c
-SRCS+=		prf.c com.c cons.c ns16550.c pciide.c wdc.c wd.c
+SRCS+=		prf.c com.c cons.c ns16550.c pciide.c tgets.c wdc.c wd.c
 
 SRCS+=		vers.c
 CLEANFILES+=	vers.c
Index: stand/boot/boot.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/boot.c,v
retrieving revision 1.3
diff -u -r1.3 boot.c
--- stand/boot/boot.c	7 Jan 2004 12:43:44 -0000	1.3
+++ stand/boot/boot.c	3 Apr 2004 00:05:36 -0000
@@ -79,6 +79,7 @@
 #include <lib/libkern/libkern.h>
 
 #include <sys/param.h>
+#include <sys/boot_flag.h>
 #include <sys/exec.h>
 #include <sys/exec_elf.h>
 
@@ -88,18 +89,18 @@
 #include "bootinfo.h"
 
 char *kernelnames[] = {
-	"netbsd",
-	"netbsd.gz",
-	"onetbsd",
-	"onetbsd.gz",
-	"netbsd.bak",
-	"netbsd.bak.gz",
-	"netbsd.old",
-	"netbsd.old.gz",
-	"netbsd.cobalt",
-	"netbsd.cobalt.gz",
-	"netbsd.elf",
-	"netbsd.elf.gz",
+	"/netbsd",
+	"/netbsd.gz",
+	"/onetbsd",
+	"/onetbsd.gz",
+	"/netbsd.bak",
+	"/netbsd.bak.gz",
+	"/netbsd.old",
+	"/netbsd.old.gz",
+	"/netbsd.cobalt",
+	"/netbsd.cobalt.gz",
+	"/netbsd.elf",
+	"/netbsd.elf.gz",
 	NULL
 };
 
@@ -108,14 +109,15 @@
 
 static char *bootstring;
 
-static int patch_bootstring	(char *bootspec);
-static int get_bsdbootname	(char **dev, char **name, char **kname);
-static int prominit		(unsigned int memsize);
-static int print_banner		(unsigned int memsize);
+static int patch_bootstring(char *);
+static int get_bsdbootname(char **, char **);
+static int parse_bootname(char *, int, char **, char **);
+static int prominit(unsigned int);
+static int print_banner(unsigned int);
 
 int cpu_reboot(void);
 
-int main(unsigned int memsize);
+int main(unsigned int);
 
 /*
  * Perform CPU reboot.
@@ -123,6 +125,7 @@
 int
 cpu_reboot()
 {
+
 	printf("rebooting...\n\n");
 
 	*(volatile char *)MIPS_PHYS_TO_KSEG1(LED_ADDR) = LED_RESET;
@@ -134,10 +137,11 @@
 /*
  * Substitute root value with NetBSD root partition name.
  */
-int
+static int
 patch_bootstring(bootspec)
 	char *bootspec;
 {
+
 	char *sp = bootstring;
 	u_int8_t unit, part;
 	int dev, error;
@@ -152,7 +156,7 @@
 	DPRINTF(("patch_bootstring: %d, %d\n", unit, part));
 
 	/* take out the 'root=xxx' parameter */
-	if ( (sp = strstr(bootstring, "root=")) != NULL) {
+	if ((sp = strstr(bootstring, "root=")) != NULL) {
 		const char *end;
 		
 		end = strchr(sp, ' ');
@@ -189,62 +193,130 @@
 /*
  * Extract NetBSD boot specification
  */
-int
-get_bsdbootname(dev, name, kname)
+static int
+get_bsdbootname(dev, kname)
 	char **dev;
-	char **name;
 	char **kname;
 {
-	char *spec;
-
-	*dev = NULL;
-	*name = NULL;
-	*kname = NULL;
-
-	if ( (spec = strstr(bootstring, "nbsd=")) != NULL) {
-		int len;
-		char *ptr = strchr(spec, ' ');
-
+	int len, error;
+	char *bootstr_dev, *bootstr_kname;
+	char *prompt_dev, *prompt_kname;
+	char *ptr, *spec;
+	char c, namebuf[PATH_MAX];
+
+	bootstr_dev = prompt_dev = NULL;
+	bootstr_kname = prompt_kname = NULL;
+
+	/* first, get bootname from bootstrings */
+	if ((spec = strstr(bootstring, "nbsd=")) != NULL) {
+		ptr = strchr(spec, ' ');
 		spec += 5; 	/* skip 'nbsd=' */
 		len = (ptr == NULL) ? strlen(spec) : ptr - spec;
-
 		if (len > 0) {
-			char *devname = alloc(len + 1);
-			if (devname != NULL) {
-				memcpy(devname, spec, len);
-				devname[len] = '\0';
-
-				if ( (ptr = memchr(devname,':',len)) != NULL) {
-					/* wdXX:kernel */
-					*ptr = '\0';
-					*dev = devname;
-
-					if (*++ptr)
-						*kname = ptr;
-
-					devname = alloc(len + 1);
-					if (devname != NULL) {
-						memcpy(devname, spec, len);
-						devname[len] = '\0';
-					}
-				}
-
-				*name = devname;
-				return (0);
-			}
+			if (parse_bootname(spec, len,
+			    &bootstr_dev, &bootstr_kname))
+				return 1;
+		}
+	}
+
+	DPRINTF(("bootstr_dev = %s, bootstr_kname = %s\n",
+	    bootstr_dev ? bootstr_dev : "<NULL>",
+	    bootstr_kname ? bootstr_kname : "<NULL>"));
+
+	spec = NULL;
+	len = 0;
+
+	memset(namebuf, 0, sizeof namebuf);
+	printf("Boot [%s:%s]: ",
+	    bootstr_dev ? bootstr_dev : DEFBOOTDEV,
+	    bootstr_kname ? bootstr_kname : DEFKERNELNAME);
+
+	if (tgets(namebuf) == -1)
+		printf("\n");
+
+	ptr = namebuf;
+	while ((c = *ptr) != '\0') {
+		while (c == ' ')
+			c = *++ptr;
+		if (c == '\0')
+			break;
+		if (c == '-') {
+			while ((c = *++ptr) && c != ' ')
+				;
+#if notyet
+			BOOT_FLAG(c, boothowto);
+#endif
+		} else {
+			spec = ptr;
+			while ((c = *++ptr) && c != ' ')
+				;
+			if (c)
+				*ptr++ = '\0';
+			len = strlen(spec);
 		}
 	}
 
-	return (1);
+	if (len > 0) {
+		if (parse_bootname(spec, len, &prompt_dev, &prompt_kname))
+			return 1;
+	}
+
+	DPRINTF(("prompt_dev = %s, prompt_kname = %s\n",
+	    prompt_dev ? prompt_dev : "<NULL>",
+	    prompt_kname ? prompt_kname : "<NULL>"));
+
+	if (prompt_dev)
+		*dev = prompt_dev;
+	else
+		*dev = bootstr_dev;
+
+	if (prompt_kname)
+		*kname = prompt_kname;
+	else
+		*kname = bootstr_kname;
+
+	DPRINTF(("dev = %s, kname = %s\n",
+	    *dev ? *dev : "<NULL>",
+	    *kname ? *kname : "<NULL>"));
+
+	return 0;
+}
+
+static int
+parse_bootname(spec, len, dev, kname)
+	char *spec;
+	int len;
+	char **dev;
+	char **kname;
+{
+	char *bootname, *ptr;
+
+	bootname = alloc(len + 1);
+	if (bootname == NULL)
+		return 1;
+	memcpy(bootname, spec, len);
+	bootname[len] = '\0';
+
+	if ((ptr = memchr(bootname, ':', len)) != NULL) {
+		/* "wdXX:kernel" */
+		*ptr = '\0';
+		*dev = bootname;
+		if (*++ptr)
+			*kname = ptr;
+	} else
+		/* "kernel" */
+		*kname = bootname;
+	return 0;
 }
 
 /*
  * Get the bootstring from PROM.
  */
-int
+static int
 prominit(memsize)
 	unsigned int memsize;
 {
+
 	bootstring = (char *)(memsize - 512);
 	bootstring[511] = '\0';
 }
@@ -252,10 +324,11 @@
 /*
  * Print boot message.
  */
-int
+static int
 print_banner(memsize)
 	unsigned int memsize;
 {
+
 	printf("\n");
 	printf(">> %s " NETBSD_VERS " Bootloader, Revision %s [@%p]\n",
 			bootprog_name, bootprog_rev, (void*)&start);
@@ -272,18 +345,15 @@
 main(memsize)
 	unsigned int memsize;
 {
-	char *name, **namep, *dev, *kernel, *spec, *bi_addr;
+	char **namep, *dev, *kernel, *bi_addr;
 	char bootpath[PATH_MAX];
-	int win;
+	int win, addr, speed;
 	u_long marks[MARK_MAX];
 	void (*entry) __P((unsigned int, u_int, char*));
-
 	struct btinfo_flags bi_flags;
 	struct btinfo_symtab bi_syms;
 	struct btinfo_bootpath bi_bpath;
 
-	int addr, speed;
-
 	/* Initialize boot info early */
 	bi_flags.bi_flags = 0x0;
 	bi_addr = bi_init();
@@ -295,31 +365,30 @@
 	print_banner(memsize);
 
 	memset(marks, 0, sizeof marks);
-	get_bsdbootname(&dev, &name, &kernel);
+	get_bsdbootname(&dev, &kernel);
 
 	if (kernel != NULL) {
 		DPRINTF(("kernel: %s\n", kernel));
-		patch_bootstring(name);
-		win = (loadfile(name, marks, LOAD_KERNEL) == 0);
+		kernelnames[0] = kernel;
+		kernelnames[1] = NULL;
 	} else {
-		win = 0;
 		DPRINTF(("kernel: NULL\n"));
-		DPRINTF(("Kernel names: %p\n", kernelnames));
-		for (namep = kernelnames, win = 0;
-				(*namep != NULL) && !win;
-				namep++) {
-			kernel = *namep;
-
-			bootpath[0] = '\0';
-
-			strcpy(bootpath, (dev != NULL) ? dev : "wd0a");
-			strcat(bootpath, ":");
-			strcat(bootpath, kernel);
-
-			printf("Loading: %s\n", bootpath);
-			patch_bootstring(bootpath);
-			win = (loadfile(bootpath, marks, LOAD_ALL) != -1);
-		}
+	}
+
+	win = 0;
+	DPRINTF(("Kernel names: %p\n", kernelnames));
+	for (namep = kernelnames, win = 0; (*namep != NULL) && !win; namep++) {
+		kernel = *namep;
+
+		bootpath[0] = '\0';
+
+		strcpy(bootpath, dev ? dev : DEFBOOTDEV);
+		strcat(bootpath, ":");
+		strcat(bootpath, kernel);
+
+		printf("Loading: %s\n", bootpath);
+		patch_bootstring(bootpath);
+		win = (loadfile(bootpath, marks, LOAD_ALL) != -1);
 	}
 
 	if (win) {
Index: stand/boot/boot.h
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/boot.h,v
retrieving revision 1.1
diff -u -r1.1 boot.h
--- stand/boot/boot.h	25 Jun 2003 17:24:22 -0000	1.1
+++ stand/boot/boot.h	3 Apr 2004 00:05:36 -0000
@@ -40,6 +40,7 @@
 #endif
 
 #define MAXDEVNAME	16
+#define DEFBOOTDEV	"wd0a"
 #define DEFKERNELNAME	kernelnames[0]
 
 extern char *kernelnames[];
@@ -74,3 +75,5 @@
  */
 int devparse (const char *fname, int *dev, u_int8_t *unit,
 			u_int8_t *part, const char **file);
+
+int tgets(char *);
--- /dev/null	2004-04-03 08:30:18.000000000 +0900
+++ stand/boot/tgets.c	2004-01-11 13:31:09.000000000 +0900
@@ -0,0 +1,95 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 1993
+ *	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.
+ *
+ *	@(#)gets.c	8.1 (Berkeley) 6/11/93
+ */
+
+#include <lib/libsa/stand.h>
+#include "boot.h"
+
+#define USE_SCAN
+
+int
+tgets(buf)
+	char *buf;
+{
+	int c;
+	char *lp;
+
+#ifdef	USE_SCAN
+	int i;
+
+#define	SCANWAIT	10000
+#define	PWAIT		500
+	for (i = 0; i < PWAIT; i++) {
+		if ((c = cnscan()) != -1)
+			goto next;
+		delay(SCANWAIT / 32); /* XXX */
+	}
+	return (-1);
+next:
+#else
+	c = getchar();
+#endif
+	for (lp = buf;; c = getchar()) {
+		switch (c & 0177) {
+		case '\n':
+		case '\r':
+			*lp = '\0';
+			putchar('\n');
+			return 0;
+		case '\b':
+		case '\177':
+			if (lp > buf) {
+				lp--;
+				putchar('\b');
+				putchar(' ');
+				putchar('\b');
+			}
+			break;
+		case 'r'&037: {
+			register char *p;
+
+			putchar('\n');
+			for (p = buf; p < lp; ++p)
+				putchar(*p);
+			break;
+		}
+		case 'u'&037:
+		case 'w'&037:
+			lp = buf;
+			putchar('\n');
+			break;
+		default:
+			*lp++ = c;
+			putchar(c);
+		}
+	}
+}