xref: /original-bsd/sys/sparc/sparc/autoconf.c (revision ae45e3e8)
1 /*
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This software was developed by the Computer Systems Engineering group
6  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7  * contributed to Berkeley.
8  *
9  * All advertising materials mentioning features or use of this software
10  * must display the following acknowledgement:
11  *	This product includes software developed by the University of
12  *	California, Lawrence Berkeley Laboratory.
13  *
14  * %sccs.include.redist.c%
15  *
16  *	@(#)autoconf.c	8.4 (Berkeley) 10/01/93
17  *
18  * from: $Header: autoconf.c,v 1.38 93/10/01 21:24:51 torek Exp $ (LBL)
19  */
20 
21 #include <sys/param.h>
22 #include <sys/map.h>
23 #include <sys/buf.h>
24 #include <sys/disklabel.h>
25 #include <sys/device.h>
26 #include <sys/disk.h>
27 #include <sys/dkstat.h>
28 #include <sys/conf.h>
29 #include <sys/dmap.h>
30 #include <sys/reboot.h>
31 #include <sys/socket.h>
32 #include <sys/systm.h>
33 
34 #include <net/if.h>
35 
36 #include <machine/autoconf.h>
37 #include <machine/bsd_openprom.h>
38 #include <machine/cpu.h>
39 
40 /*
41  * The following several variables are related to
42  * the configuration process, and are used in initializing
43  * the machine.
44  */
45 int	cold;		/* if 1, still working on cold-start */
46 int	dkn;		/* number of iostat dk numbers assigned so far */
47 int	fbnode;		/* node ID of ROM's console frame buffer */
48 int	optionsnode;	/* node ID of ROM's options */
49 
50 extern	struct promvec *promvec;
51 
52 static	int rootnode;
53 int	findroot __P((void));
54 void	setroot __P((void));
55 static	int getstr __P((char *, int));
56 static	int findblkmajor __P((struct dkdevice *));
57 static	struct device *getdisk __P((char *, int, int, dev_t *));
58 
59 struct	bootpath bootpath[8];
60 
61 /*
62  * Most configuration on the SPARC is done by matching OPENPROM Forth
63  * device names with our internal names.
64  */
65 int
66 matchbyname(parent, cf, aux)
67 	struct device *parent;
68 	struct cfdata *cf;
69 	void *aux;
70 {
71 
72 	return (strcmp(cf->cf_driver->cd_name, *(char **)aux) == 0);
73 }
74 
75 /*
76  * Convert hex ASCII string to a value.  Returns updated pointer.
77  * Depends on ASCII order (this *is* machine-dependent code, you know).
78  */
79 static char *
80 str2hex(str, vp)
81 	register char *str;
82 	register int *vp;
83 {
84 	register int v, c;
85 
86 	for (v = 0;; v = v * 16 + c, str++) {
87 		c = *(u_char *)str;
88 		if (c <= '9') {
89 			if ((c -= '0') < 0)
90 				break;
91 		} else if (c <= 'F') {
92 			if ((c -= 'A' - 10) < 10)
93 				break;
94 		} else if (c <= 'f') {
95 			if ((c -= 'a' - 10) < 10)
96 				break;
97 		} else
98 			break;
99 	}
100 	*vp = v;
101 	return (str);
102 }
103 
104 /*
105  * locore.s code calls bootstrap() just before calling main(), after double
106  * mapping the kernel to high memory and setting up the trap base register.
107  * We must finish mapping the kernel properly and glean any bootstrap info.
108  */
109 void
110 bootstrap()
111 {
112 	register char *cp, *pp;
113 	register struct bootpath *bp;
114 	int v0val[3];
115 	int nmmu, ncontext, node;
116 #ifdef KGDB
117 	extern int kgdb_debug_panic;
118 #endif
119 
120 	node = findroot();
121 	nmmu = getpropint(node, "mmu-npmg", 128);
122 	ncontext = getpropint(node, "mmu-nctx", 8);
123 	pmap_bootstrap(nmmu, ncontext);
124 #ifdef KGDB
125 	zs_kgdb_init();			/* XXX */
126 #endif
127 	/*
128 	 * On SS1s, promvec->pv_v0bootargs->ba_argv[1] contains the flags
129 	 * that were given after the boot command.  On SS2s, pv_v0bootargs
130 	 * is NULL but *promvec->pv_v2bootargs.v2_bootargs points to
131 	 * "vmunix -s" or whatever.
132 	 * ###	DO THIS BEFORE pmap_boostrap?
133 	 */
134 	bp = bootpath;
135 	if (promvec->pv_romvec_vers < 2) {
136 		/* Grab boot device name and values. */
137 		cp = (*promvec->pv_v0bootargs)->ba_argv[0];
138 		if (cp != NULL) {
139 			/* Kludge something up */
140 			pp = cp + 2;
141 			v0val[0] = v0val[1] = v0val[2] = 0;
142 			if (*pp == '(' &&
143 			    *(pp = str2hex(++pp, &v0val[0])) == ',' &&
144 			    *(pp = str2hex(++pp, &v0val[1])) == ',')
145 				(void)str2hex(++pp, &v0val[2]);
146 
147 			/* Assume sbus0 */
148 			strcpy(bp->name, "sbus");
149 			bp->val[0] = 0;
150 			++bp;
151 
152 			if (cp[0] == 'l' && cp[1] == 'e') {
153 				/* le */
154 				strcpy(bp->name, "le");
155 				bp->val[0] = -1;
156 				bp->val[1] = v0val[0];
157 			} else {
158 				/* sd or maybe st; assume espN */
159 				strcpy(bp->name, "esp");
160 				bp->val[0] = -1;
161 				bp->val[1] = v0val[0];
162 
163 /* XXX map target 0 to 3, 3 to 0. Should really see how the prom is configed */
164 #define CRAZYMAP(v) ((v) == 3 ? 0 : (v) == 0 ? 3 : (v))
165 
166 				++bp;
167 				bp->name[0] = cp[0];
168 				bp->name[1] = cp[1];
169 				bp->name[2] = '\0';
170 				bp->val[0] = CRAZYMAP(v0val[1]);
171 				bp->val[1] = v0val[2];
172 			}
173 		}
174 
175 		/* Setup pointer to boot flags */
176 		cp = (*promvec->pv_v0bootargs)->ba_argv[1];
177 		if (cp == NULL || *cp != '-')
178 			return;
179 	} else {
180 		/* Grab boot path */
181 		cp = *promvec->pv_v2bootargs.v2_bootpath;
182 		while (cp != NULL && *cp == '/') {
183 			/* Step over '/' */
184 			++cp;
185 			/* Extract name */
186 			pp = bp->name;
187 			while (*cp != '@' && *cp != '/' && *cp != '\0')
188 				*pp++ = *cp++;
189 			*pp = '\0';
190 
191 			if (*cp == '@') {
192 				cp = str2hex(++cp, &bp->val[0]);
193 				if (*cp == ',')
194 					cp = str2hex(++cp, &bp->val[1]);
195 			}
196 			++bp;
197 		}
198 
199 		/* Setup pointer to boot flags */
200 		cp = *promvec->pv_v2bootargs.v2_bootargs;
201 		if (cp == NULL)
202 			return;
203 		while (*cp != '-')
204 			if (*cp++ == '\0')
205 				return;
206 	}
207 	for (;;) {
208 		switch (*++cp) {
209 
210 		case '\0':
211 			return;
212 
213 		case 'a':
214 			boothowto |= RB_ASKNAME;
215 			break;
216 
217 		case 'b':
218 			boothowto |= RB_DFLTROOT;
219 			break;
220 
221 		case 'd':	/* kgdb - always on zs	XXX */
222 #ifdef KGDB
223 			boothowto |= RB_KDB;	/* XXX unused */
224 			kgdb_debug_panic = 1;
225 			kgdb_connect(1);
226 #else
227 			printf("kernel not compiled with KGDB\n");
228 #endif
229 			break;
230 
231 		case 's':
232 			boothowto |= RB_SINGLE;
233 			break;
234 		}
235 	}
236 }
237 
238 /*
239  * Determine mass storage and memory configuration for a machine.
240  * We get the PROM's root device and make sure we understand it, then
241  * attach it as `mainbus0'.  We also set up to handle the PROM `sync'
242  * command.
243  */
244 configure()
245 {
246 	register int node;
247 	register char *cp;
248 	struct romaux ra;
249 	void sync_crash();
250 
251 	node = findroot();
252 	cp = getpropstring(node, "device_type");
253 	if (strcmp(cp, "cpu") != 0) {
254 		printf("PROM root device type = %s\n", cp);
255 		panic("need CPU as root");
256 	}
257 	*promvec->pv_synchook = sync_crash;
258 	ra.ra_node = node;
259 	ra.ra_name = cp = "mainbus";
260 	if (!config_rootfound(cp, (void *)&ra))
261 		panic("mainbus not configured");
262 	(void)spl0();
263 	if (bootdv)
264 		printf("Found boot device %s\n", bootdv->dv_xname);
265 	cold = 0;
266 	setroot();
267 	swapconf();
268 	dumpconf();
269 }
270 
271 /*
272  * Console `sync' command.  SunOS just does a `panic: zero' so I guess
273  * no one really wants anything fancy...
274  */
275 void
276 sync_crash()
277 {
278 
279 	panic("PROM sync command");
280 }
281 
282 char *
283 clockfreq(freq)
284 	register int freq;
285 {
286 	register char *p;
287 	static char buf[10];
288 
289 	freq /= 1000;
290 	sprintf(buf, "%d", freq / 1000);
291 	freq %= 1000;
292 	if (freq) {
293 		freq += 1000;	/* now in 1000..1999 */
294 		p = buf + strlen(buf);
295 		sprintf(p, "%d", freq);
296 		*p = '.';	/* now buf = %d.%3d */
297 	}
298 	return (buf);
299 }
300 
301 /* ARGSUSED */
302 static int
303 mbprint(aux, name)
304 	void *aux;
305 	char *name;
306 {
307 	register struct romaux *ra = aux;
308 
309 	if (name)
310 		printf("%s at %s", ra->ra_name, name);
311 	if (ra->ra_paddr)
312 		printf(" %saddr 0x%x", ra->ra_iospace ? "io" : "",
313 		    (int)ra->ra_paddr);
314 	return (UNCONF);
315 }
316 
317 int
318 findroot()
319 {
320 	register int node;
321 
322 	if ((node = rootnode) == 0 && (node = nextsibling(0)) == 0)
323 		panic("no PROM root device");
324 	rootnode = node;
325 	return (node);
326 }
327 
328 /*
329  * Given a `first child' node number, locate the node with the given name.
330  * Return the node number, or 0 if not found.
331  */
332 int
333 findnode(first, name)
334 	int first;
335 	register char *name;
336 {
337 	register int node;
338 
339 	for (node = first; node; node = nextsibling(node))
340 		if (strcmp(getpropstring(node, "name"), name) == 0)
341 			return (node);
342 	return (0);
343 }
344 
345 /*
346  * Fill in a romaux.  Returns 1 on success, 0 if the register property
347  * was not the right size.
348  */
349 int
350 romprop(rp, cp, node)
351 	register struct romaux *rp;
352 	const char *cp;
353 	register int node;
354 {
355 	register int len;
356 	union { char regbuf[64]; int ireg[3]; } u;
357 	static const char pl[] = "property length";
358 
359 	len = getprop(node, "reg", (void *)u.regbuf, sizeof u.regbuf);
360 	if (len < 12) {
361 		printf("%s \"reg\" %s = %d (need 12)\n", cp, pl, len);
362 		return (0);
363 	}
364 	if (len > 12)
365 		printf("warning: %s \"reg\" %s %d > 12, excess ignored\n",
366 		    cp, pl, len);
367 	rp->ra_node = node;
368 	rp->ra_name = cp;
369 	rp->ra_iospace = u.ireg[0];
370 	rp->ra_paddr = (caddr_t)u.ireg[1];
371 	rp->ra_len = u.ireg[2];
372 	rp->ra_vaddr = (caddr_t)getpropint(node, "address", 0);
373 	len = getprop(node, "intr", (void *)&rp->ra_intr, sizeof rp->ra_intr);
374 	if (len == -1)
375 		len = 0;
376 	if (len & 7) {
377 		printf("%s \"intr\" %s = %d (need multiple of 8)\n",
378 		    cp, pl, len);
379 		len = 0;
380 	}
381 	rp->ra_nintr = len >>= 3;
382 	/* SPARCstation interrupts are not hardware-vectored */
383 	while (--len >= 0) {
384 		if (rp->ra_intr[len].int_vec) {
385 			printf("WARNING: %s interrupt %d has nonzero vector\n",
386 			    cp, len);
387 			break;
388 		}
389 	}
390 	return (1);
391 }
392 
393 /*
394  * Attach the mainbus.
395  *
396  * Our main job is to attach the CPU (the root node we got in configure())
397  * and iterate down the list of `mainbus devices' (children of that node).
398  * We also record the `node id' of the default frame buffer, if any.
399  */
400 static void
401 mainbus_attach(parent, dev, aux)
402 	struct device *parent, *dev;
403 	void *aux;
404 {
405 	register int node0, node;
406 	register const char *cp, *const *ssp, *sp;
407 #define L1A_HACK		/* XXX hack to allow L1-A during autoconf */
408 #ifdef L1A_HACK
409 	int nzs = 0, audio = 0;
410 #endif
411 	struct romaux ra;
412 	static const char *const special[] = {
413 		/* find these first (end with empty string) */
414 		"memory-error",	/* as early as convenient, in case of error */
415 		"eeprom",
416 		"counter-timer",
417 		"",
418 
419 		/* ignore these (end with NULL) */
420 		"aliases",
421 		"interrupt-enable",
422 		"memory",
423 		"openprom",
424 		"options",
425 		"packages",
426 		"virtual-memory",
427 		NULL
428 	};
429 
430 	printf("\n");
431 
432 	/* configure the cpu */
433 	node = ((struct romaux *)aux)->ra_node;
434 	ra.ra_node = node;
435 	ra.ra_name = cp = "cpu";
436 	ra.ra_paddr = 0;
437 	config_found(dev, (void *)&ra, mbprint);
438 
439 	/* remember which frame buffer, if any, is to be /dev/fb */
440 	fbnode = getpropint(node, "fb", 0);
441 
442 	/* Find the "options" node */
443 	node0 = firstchild(node);
444 	optionsnode = findnode(node0, "options");
445 	if (optionsnode == 0)
446 		panic("no options in OPENPROM");
447 
448 	/* Start at the beginning of the bootpath */
449 	ra.ra_bp = bootpath;
450 
451 	/*
452 	 * Locate and configure the ``early'' devices.  These must be
453 	 * configured before we can do the rest.  For instance, the
454 	 * EEPROM contains the Ethernet address for the LANCE chip.
455 	 * If the device cannot be located or configured, panic.
456 	 */
457 	for (ssp = special; *(sp = *ssp) != 0; ssp++) {
458 		if ((node = findnode(node0, sp)) == 0) {
459 			printf("could not find %s in OPENPROM\n", sp);
460 			panic(sp);
461 		}
462 		if (!romprop(&ra, sp, node) ||
463 		    !config_found(dev, (void *)&ra, mbprint))
464 			panic(sp);
465 	}
466 
467 	/*
468 	 * Configure the rest of the devices, in PROM order.  Skip
469 	 * PROM entries that are not for devices, or which must be
470 	 * done before we get here.
471 	 */
472 	for (node = node0; node; node = nextsibling(node)) {
473 		cp = getpropstring(node, "name");
474 		for (ssp = special; (sp = *ssp) != NULL; ssp++)
475 			if (strcmp(cp, sp) == 0)
476 				break;
477 		if (sp == NULL && romprop(&ra, cp, node)) {
478 #ifdef L1A_HACK
479 			if (strcmp(cp, "audio") == 0)
480 				audio = 1;
481 			if (strcmp(cp, "zs") == 0)
482 				nzs++;
483 			if (audio && nzs >= 2)
484 				(void) splx(11 << 8);	/* XXX */
485 #endif
486 			(void) config_found(dev, (void *)&ra, mbprint);
487 		}
488 	}
489 }
490 
491 struct cfdriver mainbuscd =
492     { NULL, "mainbus", matchbyname, mainbus_attach,
493       DV_DULL, sizeof(struct device) };
494 
495 /*
496  * findzs() is called from the zs driver (which is, at least in theory,
497  * generic to any machine with a Zilog ZSCC chip).  It should return the
498  * address of the corresponding zs channel.  It may not fail, and it
499  * may be called before the VM code can be used.  Here we count on the
500  * FORTH PROM to map in the required zs chips.
501  */
502 void *
503 findzs(zs)
504 	int zs;
505 {
506 	register int node, addr;
507 
508 	node = firstchild(findroot());
509 	while ((node = findnode(node, "zs")) != 0) {
510 		if (getpropint(node, "slave", -1) == zs) {
511 			if ((addr = getpropint(node, "address", 0)) == 0)
512 				panic("findzs: zs%d not mapped by PROM", zs);
513 			return ((void *)addr);
514 		}
515 		node = nextsibling(node);
516 	}
517 	panic("findzs: cannot find zs%d", zs);
518 	/* NOTREACHED */
519 }
520 
521 int
522 makememarr(ap, max, which)
523 	register struct memarr *ap;
524 	int max, which;
525 {
526 	struct v2rmi {
527 		int	zero;
528 		int	addr;
529 		int	len;
530 	} v2rmi[200];		/* version 2 rom meminfo layout */
531 #define	MAXMEMINFO (sizeof(v2rmi) / sizeof(*v2rmi))
532 	register struct v0mlist *mp;
533 	register int i, node, len;
534 	char *prop;
535 
536 	switch (i = promvec->pv_romvec_vers) {
537 
538 	case 0:
539 		/*
540 		 * Version 0 PROMs use a linked list to describe these
541 		 * guys.
542 		 */
543 		switch (which) {
544 
545 		case MEMARR_AVAILPHYS:
546 			mp = *promvec->pv_v0mem.v0_physavail;
547 			break;
548 
549 		case MEMARR_TOTALPHYS:
550 			mp = *promvec->pv_v0mem.v0_phystot;
551 			break;
552 
553 		default:
554 			panic("makememarr");
555 		}
556 		for (i = 0; mp != NULL; mp = mp->next, i++) {
557 			if (i >= max)
558 				goto overflow;
559 			ap->addr = (u_int)mp->addr;
560 			ap->len = mp->nbytes;
561 			ap++;
562 		}
563 		break;
564 
565 	default:
566 		printf("makememarr: hope version %d PROM is like version 2\n",
567 		    i);
568 		/* FALLTHROUGH */
569 
570 	case 2:
571 		/*
572 		 * Version 2 PROMs use a property array to describe them.
573 		 */
574 		if (max > MAXMEMINFO) {
575 			printf("makememarr: limited to %d\n", MAXMEMINFO);
576 			max = MAXMEMINFO;
577 		}
578 		if ((node = findnode(firstchild(findroot()), "memory")) == 0)
579 			panic("makememarr: cannot find \"memory\" node");
580 		switch (which) {
581 
582 		case MEMARR_AVAILPHYS:
583 			prop = "available";
584 			break;
585 
586 		case MEMARR_TOTALPHYS:
587 			prop = "reg";
588 			break;
589 
590 		default:
591 			panic("makememarr");
592 		}
593 		len = getprop(node, prop, (void *)v2rmi, sizeof v2rmi) /
594 		    sizeof(struct v2rmi);
595 		for (i = 0; i < len; i++) {
596 			if (i >= max)
597 				goto overflow;
598 			ap->addr = v2rmi[i].addr;
599 			ap->len = v2rmi[i].len;
600 			ap++;
601 		}
602 		break;
603 	}
604 
605 	/*
606 	 * Success!  (Hooray)
607 	 */
608 	if (i == 0)
609 		panic("makememarr: no memory found");
610 	return (i);
611 
612 overflow:
613 	/*
614 	 * Oops, there are more things in the PROM than our caller
615 	 * provided space for.  Truncate any extras.
616 	 */
617 	printf("makememarr: WARNING: lost some memory\n");
618 	return (i);
619 }
620 
621 /*
622  * Internal form of getprop().  Returns the actual length.
623  */
624 int
625 getprop(node, name, buf, bufsiz)
626 	int node;
627 	char *name;
628 	void *buf;
629 	register int bufsiz;
630 {
631 	register struct nodeops *no;
632 	register int len;
633 
634 	no = promvec->pv_nodeops;
635 	len = no->no_proplen(node, name);
636 	if (len > bufsiz) {
637 		printf("node %x property %s length %d > %d\n",
638 		    node, name, len, bufsiz);
639 #ifdef DEBUG
640 		panic("getprop");
641 #else
642 		return (0);
643 #endif
644 	}
645 	no->no_getprop(node, name, buf);
646 	return (len);
647 }
648 
649 /*
650  * Return a string property.  There is a (small) limit on the length;
651  * the string is fetched into a static buffer which is overwritten on
652  * subsequent calls.
653  */
654 char *
655 getpropstring(node, name)
656 	int node;
657 	char *name;
658 {
659 	register int len;
660 	static char stringbuf[32];
661 
662 	len = getprop(node, name, (void *)stringbuf, sizeof stringbuf - 1);
663 	stringbuf[len] = '\0';	/* usually unnecessary */
664 	return (stringbuf);
665 }
666 
667 /*
668  * Fetch an integer (or pointer) property.
669  * The return value is the property, or the default if there was none.
670  */
671 int
672 getpropint(node, name, deflt)
673 	int node;
674 	char *name;
675 	int deflt;
676 {
677 	register int len;
678 	char intbuf[16];
679 
680 	len = getprop(node, name, (void *)intbuf, sizeof intbuf);
681 	if (len != 4)
682 		return (deflt);
683 	return (*(int *)intbuf);
684 }
685 
686 /*
687  * OPENPROM functions.  These are here mainly to hide the OPENPROM interface
688  * from the rest of the kernel.
689  */
690 int
691 firstchild(node)
692 	int node;
693 {
694 
695 	return (promvec->pv_nodeops->no_child(node));
696 }
697 
698 int
699 nextsibling(node)
700 	int node;
701 {
702 
703 	return (promvec->pv_nodeops->no_nextnode(node));
704 }
705 
706 #ifdef RCONSOLE
707 /* Pass a string to the FORTH PROM to be interpreted */
708 void
709 rominterpret(s)
710 	register char *s;
711 {
712 
713 	if (promvec->pv_romvec_vers < 2)
714 		promvec->pv_fortheval.v0_eval(strlen(s), s);
715 	else
716 		promvec->pv_fortheval.v2_eval(s);
717 }
718 
719 /*
720  * Try to figure out where the PROM stores the cursor row & column
721  * variables.  Returns nonzero on error.
722  */
723 int
724 romgetcursoraddr(rowp, colp)
725 	register int **rowp, **colp;
726 {
727 	char buf[100];
728 
729 	/*
730 	 * line# and column# are global in older proms (rom vector < 2)
731 	 * and in some newer proms.  They are local in version 2.9.  The
732 	 * correct cutoff point is unknown, as yet; we use 2.9 here.
733 	 */
734 	if (promvec->pv_romvec_vers < 2 || promvec->pv_printrev < 0x00020009)
735 		sprintf(buf,
736 		    "' line# >body >user %x ! ' column# >body >user %x !",
737 		    rowp, colp);
738 	else
739 		sprintf(buf,
740 		    "stdout @ is my-self addr line# %x ! addr column# %x !",
741 		    rowp, colp);
742 	*rowp = *colp = NULL;
743 	rominterpret(buf);
744 	return (*rowp == NULL || *colp == NULL);
745 }
746 #endif
747 
748 volatile void
749 romhalt()
750 {
751 
752 	promvec->pv_halt();
753 	panic("PROM exit failed");
754 }
755 
756 volatile void
757 romboot(str)
758 	char *str;
759 {
760 
761 	promvec->pv_reboot(str);
762 	panic("PROM boot failed");
763 }
764 
765 callrom()
766 {
767 
768 #ifdef notdef		/* sun4c FORTH PROMs do this for us */
769 	fb_unblank();
770 #endif
771 	promvec->pv_abort();
772 }
773 
774 /*
775  * Configure swap space and related parameters.
776  */
777 swapconf()
778 {
779 	register struct swdevt *swp;
780 	register int nblks;
781 
782 	for (swp = swdevt; swp->sw_dev != NODEV; swp++)
783 		if (bdevsw[major(swp->sw_dev)].d_psize) {
784 			nblks =
785 			  (*bdevsw[major(swp->sw_dev)].d_psize)(swp->sw_dev);
786 			if (nblks != -1 &&
787 			    (swp->sw_nblks == 0 || swp->sw_nblks > nblks))
788 				swp->sw_nblks = nblks;
789 		}
790 }
791 
792 #define	DOSWAP			/* Change swdevt and dumpdev too */
793 u_long	bootdev;		/* should be dev_t, but not until 32 bits */
794 
795 #define	PARTITIONMASK	0x7
796 #define	PARTITIONSHIFT	3
797 
798 static int
799 findblkmajor(dv)
800 	register struct dkdevice *dv;
801 {
802 	register int i;
803 
804 	for (i = 0; i < nblkdev; ++i)
805 		if ((void (*)(struct buf *))bdevsw[i].d_strategy ==
806 		    dv->dk_driver->d_strategy)
807 			return (i);
808 
809 	return (-1);
810 }
811 
812 static struct device *
813 getdisk(str, len, defpart, devp)
814 	char *str;
815 	int len, defpart;
816 	dev_t *devp;
817 {
818 	register struct device *dv;
819 
820 	if ((dv = parsedisk(str, len, defpart, devp)) == NULL) {
821 		printf("use one of:");
822 		for (dv = alldevs; dv != NULL; dv = dv->dv_next)
823 			if (dv->dv_class == DV_DISK)
824 				printf(" %s[a-h]", dv->dv_xname);
825 		printf("\n");
826 	}
827 	return (dv);
828 }
829 
830 struct device *
831 parsedisk(str, len, defpart, devp)
832 	char *str;
833 	int len, defpart;
834 	dev_t *devp;
835 {
836 	register struct device *dv;
837 	register char *cp, c;
838 	int majdev, mindev, part;
839 
840 	if (len == 0)
841 		return (NULL);
842 	cp = str + len - 1;
843 	c = *cp;
844 	if (c >= 'a' && c <= 'h') {
845 		part = c - 'a';
846 		*cp = '\0';
847 	} else
848 		part = defpart;
849 
850 	for (dv = alldevs; dv != NULL; dv = dv->dv_next) {
851 		if (dv->dv_class == DV_DISK &&
852 		    strcmp(str, dv->dv_xname) == 0) {
853 			majdev = findblkmajor((struct dkdevice *)dv);
854 			if (majdev < 0)
855 				panic("parsedisk");
856 			mindev = (dv->dv_unit << PARTITIONSHIFT) + part;
857 			*devp = makedev(majdev, mindev);
858 			break;
859 		}
860 	}
861 
862 	*cp = c;
863 	return (dv);
864 }
865 
866 /*
867  * Attempt to find the device from which we were booted.
868  * If we can do so, and not instructed not to do so,
869  * change rootdev to correspond to the load device.
870  */
871 void
872 setroot()
873 {
874 	register struct swdevt *swp;
875 	register struct device *dv;
876 	register int len, majdev, mindev, part;
877 	dev_t nrootdev, nswapdev;
878 	char buf[128];
879 #ifdef DOSWAP
880 	dev_t temp;
881 #endif
882 #ifdef NFS
883 	extern int (*mountroot)(), nfs_mountroot();
884 #endif
885 
886 	if (boothowto & RB_ASKNAME) {
887 		for (;;) {
888 			printf("root device? ");
889 			len = getstr(buf, sizeof(buf));
890 #ifdef GENERIC
891 			if (len > 0 && buf[len - 1] == '*') {
892 				buf[--len] = '\0';
893 				dv = getdisk(buf, len, 1, &nrootdev);
894 				if (dv != NULL) {
895 					bootdv = dv;
896 					nswapdev = nrootdev;
897 					goto gotswap;
898 				}
899 			}
900 #endif
901 			dv = getdisk(buf, len, 0, &nrootdev);
902 			if (dv != NULL) {
903 				bootdv = dv;
904 				break;
905 			}
906 		}
907 		for (;;) {
908 			printf("swap device (default %sb)? ", bootdv->dv_xname);
909 			len = getstr(buf, sizeof(buf));
910 			if (len == 0) {
911 				nswapdev = makedev(major(nrootdev),
912 				    (minor(nrootdev) & ~ PARTITIONMASK) | 1);
913 				break;
914 			}
915 			if (getdisk(buf, len, 1, &nswapdev) != NULL)
916 				break;
917 		}
918 #ifdef GENERIC
919 gotswap:
920 #endif
921 		rootdev = nrootdev;
922 		swapdev = nswapdev;
923 		dumpdev = nswapdev;		/* ??? */
924 		swdevt[0].sw_dev = nswapdev;
925 		swdevt[1].sw_dev = NODEV;
926 		return;
927 	}
928 
929 	/* XXX currently there's no way to set RB_DFLTROOT... */
930 	if (boothowto & RB_DFLTROOT || bootdv == NULL)
931 		return;
932 
933 	switch (bootdv->dv_class) {
934 
935 #ifdef NFS
936 	case DV_IFNET:
937 		mountroot = nfs_mountroot;
938 		return;
939 #endif
940 
941 #if defined(FFS) || defined(LFS)
942 	case DV_DISK:
943 		majdev = findblkmajor((struct dkdevice *)bootdv);
944 		if (majdev < 0)
945 			return;
946 		part = 0;
947 		mindev = (bootdv->dv_unit << PARTITIONSHIFT) + part;
948 		break;
949 #endif
950 
951 	default:
952 		printf("can't figure root, hope your kernel is right\n");
953 		return;
954 	}
955 
956 	/*
957 	 * Form a new rootdev
958 	 */
959 	nrootdev = makedev(majdev, mindev);
960 	/*
961 	 * If the original rootdev is the same as the one
962 	 * just calculated, don't need to adjust the swap configuration.
963 	 */
964 	if (rootdev == nrootdev)
965 		return;
966 
967 	rootdev = nrootdev;
968 	printf("Changing root device to %s%c\n", bootdv->dv_xname, part + 'a');
969 
970 #ifdef DOSWAP
971 	mindev &= ~PARTITIONMASK;
972 	temp = NODEV;
973 	for (swp = swdevt; swp->sw_dev != NODEV; swp++) {
974 		if (majdev == major(swp->sw_dev) &&
975 		    mindev == (minor(swp->sw_dev) & ~PARTITIONMASK)) {
976 			temp = swdevt[0].sw_dev;
977 			swdevt[0].sw_dev = swp->sw_dev;
978 			swp->sw_dev = temp;
979 			break;
980 		}
981 	}
982 	if (swp->sw_dev == NODEV)
983 		return;
984 
985 	/*
986 	 * If dumpdev was the same as the old primary swap device, move
987 	 * it to the new primary swap device.
988 	 */
989 	if (temp == dumpdev)
990 		dumpdev = swdevt[0].sw_dev;
991 #endif
992 }
993 
994 static int
995 getstr(cp, size)
996 	register char *cp;
997 	register int size;
998 {
999 	register char *lp;
1000 	register int c;
1001 	register int len;
1002 
1003 	lp = cp;
1004 	len = 0;
1005 	for (;;) {
1006 		c = cngetc();
1007 		switch (c) {
1008 		case '\n':
1009 		case '\r':
1010 			printf("\n");
1011 			*lp++ = '\0';
1012 			return (len);
1013 		case '\b':
1014 		case '\177':
1015 		case '#':
1016 			if (len) {
1017 				--len;
1018 				--lp;
1019 				printf(" \b ");
1020 			}
1021 			continue;
1022 		case '@':
1023 		case 'u'&037:
1024 			len = 0;
1025 			lp = cp;
1026 			printf("\n");
1027 			continue;
1028 		default:
1029 			if (len + 1 >= size || c < ' ') {
1030 				printf("\007");
1031 				continue;
1032 			}
1033 			printf("%c", c);
1034 			++len;
1035 			*lp++ = c;
1036 		}
1037 	}
1038 }
1039