Subject: kern/10057: openbsd UserKernelConfig ported to netbsd
To: None <gnats-bugs@gnats.netbsd.org>
From: None <itojun@itojun.org>
List: netbsd-bugs
Date: 05/06/2000 09:52:28
>Number:         10057
>Category:       kern
>Synopsis:       openbsd UserKernelConfig ported to netbsd
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat May 06 09:53:00 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator:     Jun-ichiro itojun Hagino
>Release:        netbsd latest current
>Organization:
	itojun.org
>Environment:
System: NetBSD starfruit.itojun.org 1.4Y NetBSD 1.4Y (STARFRUIT) #204: Sat May 6 11:45:25 JST 2000 itojun@starfruit.itojun.org:/usr/home/itojun/NetBSD/src/sys/arch/i386/compile/STARFRUIT i386


>Description:
	attached is a openbsd's UKC (UserKernelConfig), ported to netbsd.
	it makes it possible for administrators to tweak kernel configuration
	at bootstrap time.  this is very useful when we install netbsd
	from boot floppy, onto the following cases:
	- laptops that present wacky behavior.  for example, ThinkPad 570
	  chokes with pciide at this moment.
	- legacy hardware that is located at different io/memory address than
	  whatever in INSTALL kernel.
	in the above cases, we required user to roll a special boot floppy on
	her own.  it has been obstacle to newbies.

	on boot: prompt, tap "boot -c".
	prompt "UKC>" appears.  "list", "disable N", and exit.  you will be
	able to disable certain devices.

	restrictions:
	- original code allows you to add device.  the part is #ifdef'ed out.
	  to support this, we may need to change /usr/sbin/config slightly.
	- original code allows you to find device by locator values
	  (like "disable any device on irq 10").  the functionality is
	  disabled at this moment.
	- original code lets you tweak timezone setting in kernel.  the part is
	  #ifdef'ed out.

>How-To-Repeat:
	
>Fix:
	the patch is a quickhack but works just fine.  includes /boot fix for
	i386 only (should be trivial to do the change for other architectures).

	NOTE: the code kern/subr_userconf.c itself is not MD.  it should work
	fine on any architecutures.

Index: kern/subr_autoconf.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/subr_autoconf.c,v
retrieving revision 1.50
diff -u -r1.50 subr_autoconf.c
--- subr_autoconf.c	2000/03/28 17:30:10	1.50
+++ subr_autoconf.c	2000/05/06 16:43:23
@@ -104,12 +104,25 @@
 void
 configure()
 {
+#ifdef BOOT_CONFIG
+	extern void user_config __P((void));
+#endif
 
 	TAILQ_INIT(&deferred_config_queue);
 	TAILQ_INIT(&interrupt_config_queue);
 	TAILQ_INIT(&alldevs); 
 	TAILQ_INIT(&allevents);
 
+#ifdef RB_CONFIG
+	if (boothowto & RB_CONFIG) {
+#ifdef BOOT_CONFIG
+		user_config();
+#else
+		printf("kernel does not support -c; continuing..\n");
+#endif
+	}
+#endif
+
 	/*
 	 * Do the machine-dependent portion of autoconfiguration.  This
 	 * sets the configuration machinery here in motion by "finding"
@@ -192,6 +205,11 @@
 		 */
 		if (cf->cf_fstate == FSTATE_FOUND)
 			continue;
+#ifdef FSTATE_DSTAR
+		if (cf->cf_fstate == FSTATE_DSTAR ||
+		    cf->cf_fstate == FSTATE_DNOTFOUND)
+			continue;
+#endif
 		for (p = cf->cf_parents; *p >= 0; p++)
 			if (parent->dv_cfdata == &cfdata[*p])
 				mapply(&m, cf);
Index: kern/subr_userconf.c
===================================================================
RCS file: subr_userconf.c
diff -N subr_userconf.c
--- /dev/null	Sat May  6 05:16:38 2000
+++ subr_userconf.c	Sat May  6 09:43:25 2000
@@ -0,0 +1,1386 @@
+/*	$OpenBSD: subr_userconf.c,v 1.19 2000/01/08 23:23:37 d Exp $	*/
+
+/*
+ * Copyright (c) 1996 Mats O Jansson <moj@stacken.kth.se>
+ * 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 Mats O Jansson.
+ * 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 AUTHOR ``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 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/time.h>
+
+#include <dev/cons.h>
+
+extern struct cfdata cfdata[];
+#ifdef __OpenBSD__
+extern char *locnames[];
+extern short locnamp[];
+extern short cfroots[];
+extern int cfroots_size;
+extern int pv_size;
+extern short pv[];
+extern struct timezone tz;
+#endif
+
+int userconf_base = 16;				/* Base for "large" numbers */
+int userconf_maxdev = -1;			/* # of used device slots   */
+int userconf_totdev = -1;			/* # of device slots        */
+int userconf_maxlocnames = -1;			/* # of locnames            */
+int userconf_cnt = -1;				/* Line counter for ...     */
+int userconf_lines = 12;			/* ... # of lines per page  */
+int userconf_histlen = 0;
+int userconf_histcur = 0;
+char userconf_history[1024];
+int userconf_histsz = sizeof(userconf_history);
+char userconf_argbuf[40];			/* Additional input         */
+char userconf_cmdbuf[40];			/* Command line             */
+char userconf_histbuf[40];
+
+void userconf_init __P((void));
+int userconf_more __P((void));
+void userconf_modify __P((const char *, int*));
+void userconf_hist_cmd __P((char));
+void userconf_hist_int __P((int));
+void userconf_hist_eoc __P((void));
+void userconf_pnum __P((int));
+void userconf_pdevnam __P((short));
+void userconf_pdev __P((short));
+int userconf_number __P((char *, int *));
+int userconf_device __P((char *, int *, short *, short *));
+#ifdef __OpenBSD__
+int userconf_attr __P((char *, int *));
+#endif
+void userconf_change __P((int));
+void userconf_disable __P((int));
+void userconf_enable __P((int));
+void userconf_help __P((void));
+void userconf_list __P((void));
+#ifdef __OpenBSD__
+void userconf_show __P((void));
+void userconf_common_attr_val __P((short, int *, char));
+void userconf_show_attr __P((char *));
+#endif
+void userconf_common_dev __P((char *, int, short, short, char));
+#ifdef __OpenBSD__
+void userconf_common_attr __P((char *, int, char));
+#endif
+void userconf_add_read __P((char *, char, char *, int, int *));
+#ifdef __OpenBSD__
+void userconf_add __P((char *, int, short, short));
+#endif
+int userconf_parse __P((char *));
+
+static int getsn __P((char *, int));
+
+#define UC_CHANGE 'c'
+#define UC_DISABLE 'd'
+#define UC_ENABLE 'e'
+#define UC_FIND 'f'
+#define UC_SHOW 's'
+
+char *userconf_cmds[] = {
+#ifdef __OpenBSD__
+	"add",		"a",
+#endif
+	"base",		"b",
+	"change",	"c",
+	"disable",	"d",
+	"enable",	"e",
+	"exit",		"q",
+	"find",		"f",
+	"help",		"h",
+	"list",		"l",
+	"lines",	"L",
+	"quit",		"q",
+	"show",		"s",
+#ifdef __OpenBSD__
+	"timezone",	"t",
+	"verbose",	"v",
+#endif
+	"?",		"h",
+	"",		 "",
+};
+
+void
+userconf_init()
+{
+#ifdef __OpenBSD__
+	int i = 0;
+	struct cfdata *cd;
+	int   ln;
+
+	while (cfdata[i].cf_attach != 0) {
+		userconf_maxdev = i;
+		userconf_totdev = i;
+
+		cd = &cfdata[i];
+		ln = cd->cf_locnames;
+		while (locnamp[ln] != -1) {
+			if (locnamp[ln] > userconf_maxlocnames)
+				userconf_maxlocnames = locnamp[ln];
+			ln++;
+		}
+		i++;
+	}
+
+	while (cfdata[i].cf_attach == 0) {
+		userconf_totdev = i;
+		i++;
+	}
+	userconf_totdev = userconf_totdev - 1;
+#else
+	int i;
+	struct cfdata *cf;
+
+	i = 0;
+	for (cf = cfdata; cf->cf_driver; cf++)
+		i++;
+
+	userconf_maxdev = i - 1;
+	userconf_totdev = i - 1;
+#endif
+}
+
+int
+userconf_more()
+{
+	int quit = 0;
+	char c = '\0';
+
+	if (userconf_cnt != -1) {
+		if (userconf_cnt == userconf_lines) {
+			printf("--- more ---");
+			c = cngetc();
+			userconf_cnt = 0;
+			printf("\r            \r");
+		}
+		userconf_cnt++;
+		if (c == 'q' || c == 'Q')
+			quit = 1;
+	}
+	return (quit);
+}
+
+void
+userconf_hist_cmd(cmd)
+	char cmd;
+{
+	userconf_histcur = userconf_histlen;
+	if (userconf_histcur < userconf_histsz) {
+		userconf_history[userconf_histcur] = cmd;
+		userconf_histcur++;
+	}
+}
+
+void
+userconf_hist_int(val)
+	int val;
+{
+	sprintf(userconf_histbuf," %d",val);
+	if ((userconf_histcur + strlen(userconf_histbuf)) < userconf_histsz) {
+		bcopy(userconf_histbuf,
+		      &userconf_history[userconf_histcur],
+		      strlen(userconf_histbuf));
+		userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
+	}
+}
+
+void
+userconf_hist_eoc()
+{
+	if (userconf_histcur < userconf_histsz) {
+		userconf_history[userconf_histcur] = '\n';
+		userconf_histcur++;
+		userconf_histlen = userconf_histcur;
+	}
+}
+
+void
+userconf_pnum(val)
+	int val;
+{
+	if (val > -2 && val < 16) {
+		printf("%d",val);
+	} else {
+		switch (userconf_base) {
+		case 8:
+			printf("0%o",val);
+			break;
+		case 10:
+			printf("%d",val);
+			break;
+		case 16:
+		default:
+			printf("0x%x",val);
+			break;
+		}
+	}
+}
+
+void
+userconf_pdevnam(dev)
+	short dev;
+{
+	struct cfdata *cd;
+
+	cd = &cfdata[dev];
+	printf("%s", cd->cf_driver->cd_name);
+	switch (cd->cf_fstate) {
+	case FSTATE_NOTFOUND:
+	case FSTATE_DNOTFOUND:
+		printf("%d", cd->cf_unit);
+		break;
+	case FSTATE_FOUND:
+		printf("*FOUND*");
+		break;
+	case FSTATE_STAR:
+	case FSTATE_DSTAR:
+		printf("*");
+		break;
+	default:
+		printf("*UNKNOWN*");
+		break;
+	}
+}
+
+void
+userconf_pdev(devno)
+	short devno;
+{
+	struct cfdata *cd;
+	short *p;
+	int   *l;
+#ifdef __OpenBSD__
+	int   ln;
+#else
+	const char **ln;
+#endif
+	char c;
+
+	if (devno >  userconf_maxdev) {
+		printf("Unknown devno (max is %d)\n", userconf_maxdev);
+		return;
+	}
+
+	cd = &cfdata[devno];
+
+	printf("%3d ", devno);
+	userconf_pdevnam(devno);
+	printf(" at");
+	c = ' ';
+	p = cd->cf_parents;
+	if (*p == -1)
+		printf(" root");
+	while (*p != -1) {
+		printf("%c", c);
+		userconf_pdevnam(*p++);
+		c = '|';
+	}
+	switch (cd->cf_fstate) {
+	case FSTATE_NOTFOUND:
+	case FSTATE_FOUND:
+	case FSTATE_STAR:
+		break;
+	case FSTATE_DNOTFOUND:
+	case FSTATE_DSTAR:
+		printf(" disable");
+		break;
+	default:
+		printf(" ???");
+		break;
+	}
+	l = cd->cf_loc;
+#ifdef __OpenBSD__
+	ln = cd->cf_locnames;
+	while (locnamp[ln] != -1) {
+		printf(" %s ", locnames[locnamp[ln]]);
+		ln++;
+		userconf_pnum(*l++);
+	}
+#else
+	ln = cd->cf_locnames;
+	while (ln && *ln) {
+		printf(" %s ", *ln++);
+		userconf_pnum(*l++);
+	}
+#endif
+	printf("\n");
+}
+
+int
+userconf_number(c, val)
+	char *c;
+	int *val;
+{
+	u_int num = 0;
+	int neg = 0;
+	int base = 10;
+
+	if (*c == '-') {
+		neg = 1;
+		c++;
+	}
+	if (*c == '0') {
+		base = 8;
+		c++;
+		if (*c == 'x' || *c == 'X') {
+			base = 16;
+			c++;
+		}
+	}
+	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
+		u_char cc = *c;
+
+		if (cc >= '0' && cc <= '9')
+			cc = cc - '0';
+		else if (cc >= 'a' && cc <= 'f')
+			cc = cc - 'a' + 10;
+		else if (cc >= 'A' && cc <= 'F')
+			cc = cc - 'A' + 10;
+		else
+			return (-1);
+
+		if (cc > base)
+			return (-1);
+		num = num * base + cc;
+		c++;
+	}
+
+	if (neg && num > INT_MAX)	/* overflow */
+		return (1);
+	*val = neg ? - num : num;
+	return (0);
+}
+
+int
+userconf_device(cmd, len, unit, state)
+	char *cmd;
+	int *len;
+	short *unit, *state;
+{
+	short u = 0, s = FSTATE_FOUND;
+	int l = 0;
+	char *c;
+
+	c = cmd;
+	while (*c >= 'a' && *c <= 'z') {
+		l++;
+		c++;
+	}
+	if (*c == '*') {
+		s = FSTATE_STAR;
+		c++;
+	} else {
+		while (*c >= '0' && *c <= '9') {
+			s = FSTATE_NOTFOUND;
+			u = u*10 + *c - '0';
+			c++;
+		}
+	}
+	while (*c == ' ' || *c == '\t' || *c == '\n')
+		c++;
+
+	if (*c == '\0') {
+		*len = l;
+		*unit = u;
+		*state = s;
+		return(0);
+	}
+
+	return(-1);
+}
+
+#ifdef __OpenBSD__
+int
+userconf_attr(cmd, val)
+	char *cmd;
+	int *val;
+{
+	char *c;
+	short attr = -1, i = 0, l = 0;
+
+	c = cmd;
+	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
+		c++;
+		l++;
+	}
+
+	while (i <= userconf_maxlocnames) {
+		if (strlen(locnames[i]) == l) {
+			if (strncasecmp(cmd, locnames[i], l) == 0)
+				attr = i;
+		}
+		i++;
+	}
+
+	if (attr == -1) {
+		return (-1);
+	}
+
+	*val = attr;
+
+	return(0);
+}
+#endif
+
+void
+userconf_modify(item, val)
+	const char *item;
+	int  *val;
+{
+	int ok = 0;
+	int a;
+	char *c;
+	int i;
+
+	while (!ok) {
+		printf("%s [", item);
+		userconf_pnum(*val);
+		printf("] ? ");
+
+		i = getsn(userconf_argbuf, sizeof(userconf_argbuf));
+
+		c = userconf_argbuf;
+		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
+
+		if (*c != '\0') {
+			if (userconf_number(c, &a) == 0) {
+				*val = a;
+				ok = 1;
+			} else {
+				printf("Unknown argument\n");
+			}
+		} else {
+			ok = 1;
+		}
+	}
+}
+
+void
+userconf_change(devno)
+	int devno;
+{
+	struct cfdata *cd;
+	char c = '\0';
+	int   *l;
+	int   ln;
+#ifndef __OpenBSD__
+	const char **locnames;
+#endif
+
+	if (devno <=  userconf_maxdev) {
+
+		userconf_pdev(devno);
+
+		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
+			printf("change (y/n) ?");
+			c = cngetc();
+			printf("\n");
+		}
+
+		if (c == 'y' || c == 'Y') {
+			int share = 0, i, *lk;
+			
+			/* XXX add cmd 'c' <devno> */
+			userconf_hist_cmd('c');
+			userconf_hist_int(devno);
+			
+			cd = &cfdata[devno];
+			l = cd->cf_loc;
+#ifdef __OpenBSD__
+			ln = cd->cf_locnames;
+#else
+			locnames = cd->cf_locnames;
+			ln = 0;
+#endif
+
+			/*
+			 * Search for some other driver sharing this
+			 * locator table. if one does, we may need to
+			 * replace the locators with a malloc'd copy.
+			 */
+			for (i = 0; cfdata[i].cf_driver; i++)
+				if (i != devno && cfdata[i].cf_loc == l)
+					share = 1;
+			if (share) {
+#ifdef __OpenBSD__
+				for (i = 0; locnamp[ln+i] != -1 ; i++)
+#else
+				for (i = 0; locnames[ln+i]; i++)
+#endif
+					;
+				lk = l = (int *)malloc(sizeof(int) * i,
+				    M_TEMP, M_NOWAIT);
+				bcopy(cd->cf_loc, l, sizeof(int) * i);
+			}
+
+#ifdef __OpenBSD__
+			while (locnamp[ln] != -1)
+#else
+			while (locnames[ln])
+#endif
+			{
+#ifdef __OpenBSD__
+				userconf_modify(locnames[locnamp[ln]], l);
+#else
+				userconf_modify(locnames[ln], l);
+#endif
+				
+				/* XXX add *l */
+				userconf_hist_int(*l);
+				
+				ln++;
+				l++;
+			}
+
+			if (share) {
+				if (bcmp(cd->cf_loc, lk, sizeof(int) * i))
+					cd->cf_loc = lk;
+				else
+					free(lk, M_TEMP);
+			}
+
+			printf("%3d ", devno);
+			userconf_pdevnam(devno);
+			printf(" changed\n");
+			userconf_pdev(devno);
+
+			/* XXX add eoc */
+			userconf_hist_eoc();
+			
+		}
+	} else {
+		printf("Unknown devno (max is %d)\n", userconf_maxdev);
+	}
+}
+
+void
+userconf_disable(devno)
+	int devno;
+{
+	int done = 0;
+
+	if (devno <= userconf_maxdev) {
+		switch (cfdata[devno].cf_fstate) {
+		case FSTATE_NOTFOUND:
+			cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
+			break;
+		case FSTATE_STAR:
+			cfdata[devno].cf_fstate = FSTATE_DSTAR;
+			break;
+		case FSTATE_DNOTFOUND:
+		case FSTATE_DSTAR:
+			done = 1;
+			break;
+		default:
+			printf("Error unknown state\n");
+			break;
+		}
+
+		printf("%3d ", devno);
+		userconf_pdevnam(devno);
+		if (done) {
+			printf(" already");
+		} else {
+			/* XXX add cmd 'd' <devno> eoc */
+			userconf_hist_cmd('d');
+			userconf_hist_int(devno);
+			userconf_hist_eoc();
+		}
+		printf(" disabled\n");
+	} else {
+		printf("Unknown devno (max is %d)\n", userconf_maxdev);
+	}
+}
+
+void
+userconf_enable(devno)
+	int devno;
+{
+	int done = 0;
+	
+	if (devno <= userconf_maxdev) {
+		switch (cfdata[devno].cf_fstate) {
+		case FSTATE_DNOTFOUND:
+			cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
+			break;
+		case FSTATE_DSTAR:
+			cfdata[devno].cf_fstate = FSTATE_STAR;
+			break;
+		case FSTATE_NOTFOUND:
+		case FSTATE_STAR:
+			done = 1;
+			break;
+		default:
+			printf("Error unknown state\n");
+			break;
+		}
+
+		printf("%3d ", devno);
+		userconf_pdevnam(devno);
+		if (done) {
+			printf(" already");
+		} else {
+			/* XXX add cmd 'e' <devno> eoc */
+			userconf_hist_cmd('d');
+			userconf_hist_int(devno);
+			userconf_hist_eoc();
+		}
+		printf(" enabled\n");
+	} else {
+		printf("Unknown devno (max is %d)\n", userconf_maxdev);
+	}
+}
+
+void
+userconf_help()
+{
+	int j = 0, k;
+
+	printf("command   args                description\n");
+	while (*userconf_cmds[j] != '\0') {
+		printf(userconf_cmds[j]);
+		k = strlen(userconf_cmds[j]);
+		while (k < 10) {
+			printf(" ");
+			k++;
+		}
+		switch (*userconf_cmds[j+1]) {
+		case 'L':
+			printf("[count]             number of lines before more");
+			break;
+#ifdef __OpenBSD__
+		case 'a':
+			printf("dev                 add a device");
+			break;
+#endif
+		case 'b':
+			printf("8|10|16             base on large numbers");
+			break;
+		case 'c':
+			printf("devno|dev           change devices");
+			break;
+		case 'd':
+#ifdef __OpenBSD__
+			printf("attr val|devno|dev  disable devices");
+#else
+			printf("devno|dev  disable devices");
+#endif
+			break;
+		case 'e':
+#ifdef __OpenBSD__
+			printf("attr val|devno|dev  enable devices");
+#else
+			printf("devno|dev  enable devices");
+#endif
+			break;
+		case 'f':
+			printf("devno|dev           find devices");
+			break;
+		case 'h':
+			printf("                    this message");
+			break;
+		case 'l':
+			printf("                    list configuration");
+			break;
+		case 'q':
+			printf("                    leave UKC");
+			break;
+#ifdef __OpenBSD__
+		case 's':
+			printf("[attr [val]]        %s",
+			   "show attributes (or devices with an attribute)");
+			break;
+		case 't':
+			printf("[mins [dst]]        set timezone/dst");
+			break;
+		case 'v':
+			printf("                    toggle verbose booting");
+			break;
+#endif
+		default:
+			printf("                    don't know");
+			break;
+		}
+		printf("\n");
+		j += 2;
+	}
+}
+
+void
+userconf_list()
+{
+	int i = 0;
+
+	userconf_cnt = 0;
+
+	while (cfdata[i].cf_attach != 0) {
+		if (userconf_more())
+			break;
+		userconf_pdev(i++);
+	}
+
+	userconf_cnt = -1;
+}
+
+#ifdef __OpenBSD__
+void
+userconf_show()
+{
+	int i = 0;
+
+	userconf_cnt = 0;
+
+	while (i <= userconf_maxlocnames) {
+		if (userconf_more())
+			break;
+		printf("%s\n", locnames[i++]);
+	}
+
+	userconf_cnt = -1;
+}
+
+void
+userconf_common_attr_val(attr, val, routine)
+	short attr;
+	int   *val;
+	char  routine;
+{
+	struct cfdata *cd;
+	int   *l;
+	int   ln;
+	int i = 0, quit = 0;
+
+	userconf_cnt = 0;
+
+	while (i <= userconf_maxdev) {
+		cd = &cfdata[i];
+		l = cd->cf_loc;
+		ln = cd->cf_locnames;
+		while (locnamp[ln] != -1) {
+			if (locnamp[ln] == attr) {
+				if (val == NULL) {
+					quit = userconf_more();
+					userconf_pdev(i);
+				} else {
+					if (*val == *l) {
+						quit = userconf_more();
+						switch (routine) {
+						case UC_ENABLE:
+							userconf_enable(i);
+							break;
+						case UC_DISABLE:
+							userconf_disable(i);
+							break;
+						case UC_SHOW:
+							userconf_pdev(i);
+							break;
+						default:
+							printf("Unknown routine /%c/\n",
+							    routine);
+							break;
+						}
+					}
+				}
+			}
+			if (quit)
+				break;
+			ln++;
+			l++;
+		}
+		if (quit)
+			break;
+		i++;
+	}
+
+	userconf_cnt = -1;
+}
+
+void
+userconf_show_attr(cmd)
+	char *cmd;
+{
+	char *c;
+	short attr = -1, i = 0, l = 0;
+	int a;
+
+	c = cmd;
+	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
+		c++;
+		l++;
+	}
+	while (*c == ' ' || *c == '\t' || *c == '\n') {
+		c++;
+	}
+	while (i <= userconf_maxlocnames) {
+		if (strlen(locnames[i]) == l) {
+			if (strncasecmp(cmd, locnames[i], l) == 0) {
+				attr = i;
+			}
+		}
+		i++;
+	}
+
+	if (attr == -1) {
+		printf("Unknown attribute\n");
+		return;
+	}
+
+	if (*c == '\0') {
+		userconf_common_attr_val(attr, NULL, UC_SHOW);
+	} else {
+		if (userconf_number(c, &a) == 0) {
+			userconf_common_attr_val(attr, &a, UC_SHOW);
+		} else {
+			printf("Unknown argument\n");
+		}
+	}
+}
+#endif
+
+void
+userconf_common_dev(dev, len, unit, state, routine)
+	char *dev;
+	int len;
+	short unit, state;
+	char routine;
+{
+	int i = 0;
+
+	switch (routine) {
+	case UC_CHANGE:
+		break;
+	default:
+		userconf_cnt = 0;
+		break;
+	}
+
+	while (cfdata[i].cf_attach != 0) {
+		if (strlen(cfdata[i].cf_driver->cd_name) == len) {
+
+			/*
+			 * Ok, if device name is correct
+			 *  If state == FSTATE_FOUND, look for "dev"
+			 *  If state == FSTATE_STAR, look for "dev*"
+			 *  If state == FSTATE_NOTFOUND, look for "dev0"
+			 */
+			if (strncasecmp(dev, cfdata[i].cf_driver->cd_name,
+					len) == 0 &&
+			    (state == FSTATE_FOUND ||
+			     (state == FSTATE_STAR &&
+			      (cfdata[i].cf_fstate == FSTATE_STAR ||
+			       cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
+			     (state == FSTATE_NOTFOUND &&
+			      cfdata[i].cf_unit == unit &&
+			      (cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
+			       cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
+				if (userconf_more())
+					break;
+				switch (routine) {
+				case UC_CHANGE:
+					userconf_change(i);
+					break;
+				case UC_ENABLE:
+					userconf_enable(i);
+					break;
+				case UC_DISABLE:
+					userconf_disable(i);
+					break;
+				case UC_FIND:
+					userconf_pdev(i);
+					break;
+				default:
+					printf("Unknown routine /%c/\n",
+					    routine);
+					break;
+				}
+			}
+		}
+		i++;
+	}
+
+	switch (routine) {
+	case UC_CHANGE:
+		break;
+	default:
+		userconf_cnt = -1;
+		break;
+	}
+}
+
+#ifdef __OpenBSD__
+void
+userconf_common_attr(cmd, attr, routine)
+	char *cmd;
+	int attr;
+	char routine;
+{
+	char *c;
+	short l = 0;
+	int a;
+
+	c = cmd;
+	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
+		c++;
+		l++;
+	}
+	while (*c == ' ' || *c == '\t' || *c == '\n') {
+		c++;
+	}
+	if (*c == '\0') {
+		printf("Value missing for attribute\n");
+		return;
+	}
+
+	if (userconf_number(c, &a) == 0) {
+		userconf_common_attr_val(attr, &a, routine);
+	} else {
+		printf("Unknown argument\n");
+	}
+}
+#endif
+
+void
+userconf_add_read(prompt, field, dev, len, val)
+	char *prompt;
+	char field;
+	char *dev;
+	int len;
+	int *val;
+{
+	int ok = 0;
+	int a;
+	char *c;
+	int i;
+
+	*val = -1;
+
+	while (!ok) {
+		printf("%s ? ", prompt);
+
+		i = getsn(userconf_argbuf, sizeof(userconf_argbuf));
+
+		c = userconf_argbuf;
+		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
+
+		if (*c != '\0') {
+			if (userconf_number(c, &a) == 0) {
+				if (a > userconf_maxdev) {
+					printf("Unknown devno (max is %d)\n",
+					    userconf_maxdev);
+				} else if (strncasecmp(dev,
+				    cfdata[a].cf_driver->cd_name, len) != 0 &&
+					field == 'a') {
+					printf("Not same device type\n");
+				} else {
+					*val = a;
+					ok = 1;
+				}
+			} else if (*c == '?') {
+				userconf_common_dev(dev, len, 0,
+				    FSTATE_FOUND, UC_FIND);
+			} else if (*c == 'q' || *c == 'Q') {
+				ok = 1;
+			} else {
+				printf("Unknown argument\n");
+			}
+		} else {
+			ok = 1;
+		}
+	}
+}
+
+#ifdef __OpenBSD__
+void
+userconf_add(dev, len, unit, state)
+	char *dev;
+	int len;
+	short unit, state;
+{
+	int i = 0, found = 0;
+	struct cfdata new;
+	int  val, max_unit, orig;
+
+	bzero(&new, sizeof(struct cfdata));
+
+	if (userconf_maxdev == userconf_totdev) {
+		printf("No more space for new devices.\n");
+		return;
+	}
+
+	if (state == FSTATE_FOUND) {
+		printf("Device not complete number or * is missing\n");
+		return;
+	}
+
+	for (i = 0; cfdata[i].cf_driver; i++)
+		if (strlen(cfdata[i].cf_driver->cd_name) == len &&
+		    strncasecmp(dev, cfdata[i].cf_driver->cd_name, len) == 0)
+			found = 1;
+
+	if (!found) {
+		printf("No device of this type exists.\n");
+		return;
+	}
+
+	userconf_add_read("Clone Device (DevNo, 'q' or '?')",
+	    'a', dev, len, &val);
+
+	if (val != -1) {
+		orig = val;
+		new = cfdata[val];
+		new.cf_unit = unit;
+		new.cf_fstate = state;
+		userconf_add_read("Insert before Device (DevNo, 'q' or '?')",
+		    'i', dev, len, &val);
+	}
+
+	if (val != -1) {
+		
+		/* XXX add cmd 'a' <orig> <val> eoc */
+		userconf_hist_cmd('a');
+		userconf_hist_int(orig);
+		userconf_hist_int(unit);
+		userconf_hist_int(state);
+		userconf_hist_int(val);
+		userconf_hist_eoc();
+		
+		/* Insert the new record */
+		for (i = userconf_maxdev; val <= i; i--)
+			cfdata[i+1] = cfdata[i];
+		cfdata[val] = new;
+
+		/* Fix indexs in pv */
+		for (i = 0; i < pv_size; i++) {
+			if ((pv[i] != -1) && (pv[i] >= val))
+				pv[i] = pv[i]++;
+		}
+
+		/* Fix indexs in cfroots */
+		for (i = 0; i < cfroots_size; i++) {
+			if ((cfroots[i] != -1) && (cfroots[i] >= val))
+				cfroots[i] = cfroots[i]++;
+		}
+
+		userconf_maxdev++;
+
+		max_unit = -1;
+
+		/* Find max unit number of the device type */
+
+		i = 0;
+		while (cfdata[i].cf_attach != 0) {
+			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
+			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
+			    len) == 0) {
+				switch (cfdata[i].cf_fstate) {
+				case FSTATE_NOTFOUND:
+				case FSTATE_DNOTFOUND:
+					if (cfdata[i].cf_unit > max_unit)
+						max_unit = cfdata[i].cf_unit;
+					break;
+				default:
+					break;
+				}
+			}
+			i++;
+		}
+
+		/* For all * entries set unit number to max+1 */
+
+		max_unit++;
+
+		i = 0;
+		while (cfdata[i].cf_attach != 0) {
+			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
+			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
+			    len) == 0) {
+				switch (cfdata[i].cf_fstate) {
+				case FSTATE_STAR:
+				case FSTATE_DSTAR:
+					cfdata[i].cf_unit = max_unit;
+					break;
+				default:
+					break;
+				}
+			}
+			i++;
+		}
+		userconf_pdev(val);
+	}
+
+	/* cf_attach, cf_driver, cf_unit, cf_state, cf_loc, cf_flags,
+	   cf_parents, cf_locnames, cf_locnames and cf_ivstubs */
+}
+#endif
+
+int
+userconf_parse(cmd)
+	char *cmd;
+{
+	char *c, *v;
+	int i = 0, j = 0, k, a;
+	short unit, state;
+
+	c = cmd;
+	while (*c == ' ' || *c == '\t')
+		c++;
+	v = c;
+	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
+		c++;
+		i++;
+	}
+
+	k = -1;
+	while (*userconf_cmds[j] != '\0') {
+		if (strlen(userconf_cmds[j]) == i) {
+			if (strncasecmp(v, userconf_cmds[j], i) == 0)
+				k = j;
+		}
+		j += 2;
+	}
+
+	while (*c == ' ' || *c == '\t' || *c == '\n')
+		c++;
+
+	if (k == -1) {
+		if (*v != '\n')
+			printf("Unknown command, try help\n");
+	} else {
+		switch (*userconf_cmds[k+1]) {
+		case 'L':
+			if (*c == '\0')
+				printf("Argument expected\n");
+			else if (userconf_number(c, &a) == 0)
+				userconf_lines = a;
+			else
+				printf("Unknown argument\n");
+			break;
+#ifdef __OpenBSD__
+		case 'a':
+			if (*c == '\0')
+				printf("Dev expected\n");
+			else if (userconf_device(c, &a, &unit, &state) == 0)
+				userconf_add(c, a, unit, state);
+			else
+				printf("Unknown argument\n");
+			break;
+#endif
+		case 'b':
+			if (*c == '\0')
+				printf("8|10|16 expected\n");
+			else if (userconf_number(c, &a) == 0) {
+				if (a == 8 || a == 10 || a == 16) {
+					userconf_base = a;
+				} else {
+					printf("8|10|16 expected\n");
+				}
+			} else
+				printf("Unknown argument\n");
+			break;
+		case 'c':
+			if (*c == '\0')
+				printf("DevNo or Dev expected\n");
+			else if (userconf_number(c, &a) == 0)
+				userconf_change(a);
+			else if (userconf_device(c, &a, &unit, &state) == 0)
+				userconf_common_dev(c, a, unit, state, UC_CHANGE);
+			else
+				printf("Unknown argument\n");
+			break;
+		case 'd':
+			if (*c == '\0')
+				printf("Attr, DevNo or Dev expected\n");
+#ifdef __OpenBSD__
+			else if (userconf_attr(c, &a) == 0)
+				userconf_common_attr(c, a, UC_DISABLE);
+#endif
+			else if (userconf_number(c, &a) == 0)
+				userconf_disable(a);
+			else if (userconf_device(c, &a, &unit, &state) == 0)
+				userconf_common_dev(c, a, unit, state, UC_DISABLE);
+			else
+				printf("Unknown argument\n");
+			break;
+		case 'e':
+			if (*c == '\0')
+				printf("Attr, DevNo or Dev expected\n");
+#ifdef __OpenBSD__
+			else if (userconf_attr(c, &a) == 0)
+				userconf_common_attr(c, a, UC_ENABLE);
+#endif
+			else if (userconf_number(c, &a) == 0)
+				userconf_enable(a);
+			else if (userconf_device(c, &a, &unit, &state) == 0)
+				userconf_common_dev(c, a, unit, state, UC_ENABLE);
+			else
+				printf("Unknown argument\n");
+			break;
+		case 'f':
+			if (*c == '\0')
+				printf("DevNo or Dev expected\n");
+			else if (userconf_number(c, &a) == 0)
+				userconf_pdev(a);
+			else if (userconf_device(c, &a, &unit, &state) == 0)
+				userconf_common_dev(c, a, unit, state, UC_FIND);
+			else
+				printf("Unknown argument\n");
+			break;
+		case 'h':
+			userconf_help();
+			break;
+		case 'l':
+			if (*c == '\0')
+				userconf_list();
+			else
+				printf("Unknown argument\n");
+			break;
+		case 'q':
+			/* XXX add cmd 'q' eoc */
+			userconf_hist_cmd('q');
+			userconf_hist_eoc();
+			return(-1);
+			break;
+		case 's':
+#ifdef __OpenBSD__
+			if (*c == '\0')
+				userconf_show();
+			else
+				userconf_show_attr(c);
+			break;
+		case 't':
+			if (*c == '\0' || userconf_number(c, &a) == 0) {
+				if (*c != '\0') {
+				        tz.tz_minuteswest = a;
+					while (*c != '\n' && *c != '\t' && 
+					    *c != ' ' && *c != '\0')
+						c++;
+					while (*c == '\t' || *c == ' ')
+						c++;
+					if (*c != '\0' && 
+					    userconf_number(c, &a) == 0)
+						tz.tz_dsttime = a;
+					userconf_hist_cmd('t');
+					userconf_hist_int(tz.tz_minuteswest);
+					userconf_hist_int(tz.tz_dsttime);
+					userconf_hist_eoc();
+				}
+				printf("timezone = %d, dst = %d\n",
+				    tz.tz_minuteswest, tz.tz_dsttime);
+			} else 
+				printf("Unknown argument\n");
+			break;
+#endif
+#ifdef __OpenBSD__
+		case 'v':
+			autoconf_verbose = !autoconf_verbose;
+			printf("autoconf verbose %sabled\n",
+			    autoconf_verbose ? "en" : "dis");
+			break;
+#endif
+		default:
+			printf("Unknown command\n");
+			break;
+		}
+	}
+	return(0);
+}
+
+extern void user_config __P((void));
+
+void
+user_config()
+{
+	char prompt[] = "UKC> ";
+
+	userconf_init();
+	printf("User Kernel Config\n");
+
+	while (1) {
+		printf(prompt);
+		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
+		    userconf_parse(userconf_cmdbuf))
+			break;
+	}
+	printf("Continuing...\n");
+}
+
+/*
+ * XXX shouldn't this be a common function?
+ */
+static int
+getsn(cp, size)
+	char *cp;
+	int size;
+{
+	char *lp;
+	int c, len;
+
+	cnpollc(1);
+
+	lp = cp;
+	len = 0;
+	for (;;) {
+		c = cngetc();
+		switch (c) {
+		case '\n':
+		case '\r':
+			printf("\n");
+			*lp++ = '\0';
+			cnpollc(0);
+			return (len);
+		case '\b':
+		case '\177':
+		case '#':
+			if (len) {
+				--len;
+				--lp;
+				printf("\b \b");
+			}
+			continue;
+		case '@':
+		case 'u'&037:
+			len = 0;
+			lp = cp;
+			printf("\n");
+			continue;
+		default:
+			if (len + 1 >= size || c < ' ') {
+				printf("\007");
+				continue;
+			}
+			printf("%c", c);
+			++len;
+			*lp++ = c;
+		}
+	}
+}
Index: sys/device.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/device.h,v
retrieving revision 1.37
diff -u -r1.37 device.h
--- device.h	2000/03/06 02:48:51	1.37
+++ device.h	2000/05/06 16:43:25
@@ -110,6 +110,8 @@
 #define FSTATE_NOTFOUND	0	/* has not been found */
 #define	FSTATE_FOUND	1	/* has been found */
 #define	FSTATE_STAR	2	/* duplicable */
+#define FSTATE_DNOTFOUND 3	/* has not been found, and is disabled */
+#define FSTATE_DSTAR	4	/* duplicable, and is disabled */
 
 typedef int (*cfmatch_t) __P((struct device *, struct cfdata *, void *));
 
Index: sys/reboot.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/reboot.h,v
retrieving revision 1.16
diff -u -r1.16 reboot.h
--- reboot.h	1998/06/06 20:24:18	1.16
+++ reboot.h	2000/05/06 16:43:26
@@ -56,6 +56,7 @@
 #define	RB_MINIROOT	0x200	/* mini-root present in memory at boot time */
 #define	RB_STRING	0x400	/* use provided bootstr */
 #define	RB_POWERDOWN	(RB_HALT|0x800) /* turn power off (or at least halt) */
+#define	RB_CONFIG	0x1000	/* change configured devices */
 
 /*
  * Constants for converting boot-style device number to type,
Index: conf/files
===================================================================
RCS file: /cvsroot/syssrc/sys/conf/files,v
retrieving revision 1.366
diff -u -r1.366 files
--- files	2000/05/02 06:30:49	1.366
+++ files	2000/05/06 16:43:28
@@ -671,6 +671,7 @@
 file kern/subr_prf.c
 file kern/subr_prof.c
 file kern/subr_rmap.c
+file kern/subr_userconf.c		boot_config
 file kern/subr_xxx.c
 file kern/sys_generic.c
 file kern/sys_process.c
Index: arch/i386/stand/lib/netbsd_opts.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/i386/stand/lib/netbsd_opts.c,v
retrieving revision 1.2
diff -u -r1.2 netbsd_opts.c
--- netbsd_opts.c	1997/03/14 06:55:07	1.2
+++ netbsd_opts.c	2000/05/06 16:43:28
@@ -51,6 +51,9 @@
 	{'s', RB_SINGLE},
 	{'r', RB_DFLTROOT},
 	{'d', RB_KDB},
+#ifdef RB_CONFIG
+	{'c', RB_CONFIG},
+#endif
 };
 
 #define OPTTABSIZE (sizeof(opttab)/sizeof(struct optent))
>Release-Note:
>Audit-Trail:
>Unformatted: