autoconf.c revision 1.11 1 /* $NetBSD: autoconf.c,v 1.11 1996/10/03 18:42:41 cgd Exp $ */
2
3 /*
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This software was developed by the Computer Systems Engineering group
8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9 * contributed to Berkeley.
10 *
11 * All advertising materials mentioning features or use of this software
12 * must display the following acknowledgement:
13 * This product includes software developed by the University of
14 * California, Lawrence Berkeley Laboratory.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 *
44 * @(#)autoconf.c 8.4 (Berkeley) 10/1/93
45 */
46
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/buf.h>
50 #include <sys/disklabel.h>
51 #include <sys/conf.h>
52 #include <sys/reboot.h>
53 #include <sys/device.h>
54
55 #include <machine/autoconf.h>
56 #include <machine/prom.h>
57
58 extern char root_device[17]; /* XXX */
59
60 struct device *booted_device;
61 int booted_partition;
62 struct bootdev_data *bootdev_data;
63 char boot_dev[128];
64
65 void parse_prom_bootdev __P((void));
66 int atoi __P((char *));
67
68 static struct device *parsedisk __P((char *str, int len, int defpart,
69 dev_t *devp));
70 static struct device *getdisk __P((char *str, int len, int defpart,
71 dev_t *devp));
72 static int findblkmajor __P((struct device *dv));
73 static char *findblkname __P((int));
74 static int getstr __P((char *cp, int size));
75
76 /*
77 * configure:
78 * called at boot time, configure all devices on system
79 */
80 void
81 configure()
82 {
83 extern int cold;
84
85 parse_prom_bootdev();
86
87 (void)splhigh();
88 if (config_rootfound("mainbus", "mainbus") == NULL)
89 panic("no mainbus found");
90 (void)spl0();
91
92 if (booted_device == NULL)
93 printf("WARNING: can't figure what device matches \"%s\"\n",
94 boot_dev);
95 setroot();
96 swapconf();
97 cold = 0;
98 }
99
100 /*
101 * Configure swap space and related parameters.
102 */
103 swapconf()
104 {
105 struct swdevt *swp;
106 int nblks, maj;
107
108 for (swp = swdevt; swp->sw_dev != NODEV; swp++) {
109 maj = major(swp->sw_dev);
110 if (maj > nblkdev)
111 break;
112 if (bdevsw[maj].d_psize) {
113 nblks = (*bdevsw[maj].d_psize)(swp->sw_dev);
114 if (nblks != -1 &&
115 (swp->sw_nblks == 0 || swp->sw_nblks > nblks))
116 swp->sw_nblks = nblks;
117 swp->sw_nblks = ctod(dtoc(swp->sw_nblks));
118 }
119 }
120 dumpconf();
121 }
122
123 struct nam2blk {
124 char *name;
125 int maj;
126 } nam2blk[] = {
127 { "st", 2 },
128 { "cd", 3 },
129 { "rd", 6 },
130 { "sd", 8 },
131 #if 0
132 { "fd", XXX },
133 #endif
134 };
135
136 static int
137 findblkmajor(dv)
138 struct device *dv;
139 {
140 char *name = dv->dv_xname;
141 register int i;
142
143 for (i = 0; i < sizeof(nam2blk)/sizeof(nam2blk[0]); ++i)
144 if (strncmp(name, nam2blk[i].name, strlen(nam2blk[0].name))
145 == 0)
146 return (nam2blk[i].maj);
147 return (-1);
148 }
149
150 static char *
151 findblkname(maj)
152 int maj;
153 {
154 register int i;
155
156 for (i = 0; i < sizeof(nam2blk)/sizeof(nam2blk[0]); ++i)
157 if (maj == nam2blk[i].maj)
158 return (nam2blk[i].name);
159 return (NULL);
160 }
161
162 static struct device *
163 getdisk(str, len, defpart, devp)
164 char *str;
165 int len, defpart;
166 dev_t *devp;
167 {
168 register struct device *dv;
169
170 if ((dv = parsedisk(str, len, defpart, devp)) == NULL) {
171 printf("use one of:");
172 for (dv = alldevs.tqh_first; dv != NULL;
173 dv = dv->dv_list.tqe_next) {
174 if (dv->dv_class == DV_DISK)
175 printf(" %s[a-h]", dv->dv_xname);
176 #ifdef NFSCLIENT
177 if (dv->dv_class == DV_IFNET)
178 printf(" %s", dv->dv_xname);
179 #endif
180 }
181 printf(" halt\n");
182 }
183 return (dv);
184 }
185
186 static struct device *
187 parsedisk(str, len, defpart, devp)
188 char *str;
189 int len, defpart;
190 dev_t *devp;
191 {
192 register struct device *dv;
193 register char *cp, c;
194 int majdev, part;
195
196 if (len == 0)
197 return (NULL);
198
199 if (len == 4 && !strcmp(str, "halt"))
200 boot(RB_HALT, NULL);
201
202 cp = str + len - 1;
203 c = *cp;
204 if (c >= 'a' && c <= ('a' + MAXPARTITIONS - 1)) {
205 part = c - 'a';
206 *cp = '\0';
207 } else
208 part = defpart;
209
210 for (dv = alldevs.tqh_first; dv != NULL; dv = dv->dv_list.tqe_next) {
211 if (dv->dv_class == DV_DISK &&
212 strcmp(str, dv->dv_xname) == 0) {
213 majdev = findblkmajor(dv);
214 if (majdev < 0)
215 panic("parsedisk");
216 *devp = MAKEDISKDEV(majdev, dv->dv_unit, part);
217 break;
218 }
219 #ifdef NFSCLIENT
220 if (dv->dv_class == DV_IFNET &&
221 strcmp(str, dv->dv_xname) == 0) {
222 *devp = NODEV;
223 break;
224 }
225 #endif
226 }
227
228 *cp = c;
229 return (dv);
230 }
231
232 /*
233 * Attempt to find the device from which we were booted.
234 * If we can do so, and not instructed not to do so,
235 * change rootdev to correspond to the load device.
236 *
237 * XXX Actually, swap and root must be on the same type of device,
238 * (ie. DV_DISK or DV_IFNET) because of how (*mountroot) is written.
239 * That should be fixed.
240 */
241 setroot()
242 {
243 struct swdevt *swp;
244 struct device *dv;
245 register int len;
246 dev_t nrootdev, nswapdev = NODEV;
247 char buf[128], *rootdevname;
248 extern int (*mountroot) __P((void *));
249 dev_t temp;
250 struct device *bootdv, *rootdv, *swapdv;
251 int bootpartition;
252 #if defined(NFSCLIENT)
253 extern char *nfsbootdevname;
254 extern int nfs_mountroot __P((void *));
255 #endif
256 #if defined(FFS)
257 extern int ffs_mountroot __P((void *));
258 #endif
259
260 bootdv = booted_device;
261 bootpartition = booted_partition;
262
263 /*
264 * If 'swap generic' and we couldn't determine root device,
265 * ask the user.
266 */
267 if (mountroot == NULL && bootdv == NULL)
268 boothowto |= RB_ASKNAME;
269
270 if (boothowto & RB_ASKNAME) {
271 for (;;) {
272 printf("root device");
273 if (bootdv != NULL) {
274 printf(" (default %s", bootdv->dv_xname);
275 if (bootdv->dv_class == DV_DISK)
276 printf("%c", bootpartition + 'a');
277 printf(")");
278 }
279 printf(": ");
280 len = getstr(buf, sizeof(buf));
281 if (len == 0 && bootdv != NULL) {
282 strcpy(buf, bootdv->dv_xname);
283 len = strlen(buf);
284 }
285 if (len > 0 && buf[len - 1] == '*') {
286 buf[--len] = '\0';
287 dv = getdisk(buf, len, 1, &nrootdev);
288 if (dv != NULL) {
289 rootdv = dv;
290 nswapdev = nrootdev;
291 goto gotswap;
292 }
293 }
294 dv = getdisk(buf, len, bootpartition, &nrootdev);
295 if (dv != NULL) {
296 rootdv = dv;
297 break;
298 }
299 }
300
301 /*
302 * because swap must be on same device type as root, for
303 * network devices this is easy.
304 */
305 if (rootdv->dv_class == DV_IFNET) {
306 swapdv = NULL;
307 goto gotswap;
308 }
309 for (;;) {
310 printf("swap device");
311 printf(" (default %s", rootdv->dv_xname);
312 if (rootdv->dv_class == DV_DISK)
313 printf("b");
314 printf(")");
315 printf(": ");
316 len = getstr(buf, sizeof(buf));
317 if (len == 0) {
318 switch (rootdv->dv_class) {
319 case DV_IFNET:
320 nswapdev = NODEV;
321 break;
322 case DV_DISK:
323 nswapdev = MAKEDISKDEV(major(nrootdev),
324 DISKUNIT(nrootdev), 1);
325 break;
326 case DV_TAPE:
327 case DV_TTY:
328 case DV_DULL:
329 case DV_CPU:
330 break;
331 }
332 swapdv = rootdv;
333 break;
334 }
335 dv = getdisk(buf, len, 1, &nswapdev);
336 if (dv) {
337 if (dv->dv_class == DV_IFNET)
338 nswapdev = NODEV;
339 swapdv = dv;
340 break;
341 }
342 }
343 gotswap:
344 rootdev = nrootdev;
345 dumpdev = nswapdev;
346 swdevt[0].sw_dev = nswapdev;
347 swdevt[1].sw_dev = NODEV;
348 } else if (mountroot == NULL) {
349 int majdev;
350
351 /*
352 * "swap generic"
353 */
354 majdev = findblkmajor(bootdv);
355 if (majdev >= 0) {
356 /*
357 * Root and swap are on a disk.
358 */
359 rootdv = swapdv = bootdv;
360 rootdev = MAKEDISKDEV(majdev, bootdv->dv_unit,
361 bootpartition);
362 nswapdev = dumpdev =
363 MAKEDISKDEV(majdev, bootdv->dv_unit, 1);
364 } else {
365 /*
366 * Root and swap are on a net.
367 */
368 rootdv = swapdv = bootdv;
369 nswapdev = dumpdev = NODEV;
370 }
371 swdevt[0].sw_dev = nswapdev;
372 swdevt[1].sw_dev = NODEV;
373 } else {
374 /*
375 * `root DEV swap DEV': honour rootdev/swdevt.
376 * rootdev/swdevt/mountroot already properly set.
377 */
378
379 rootdevname = findblkname(major(rootdev));
380 if (rootdevname == NULL) {
381 /* Root on NFS or unknown device. */
382 strcpy(root_device, "??");
383 } else {
384 /* Root on known block device. */
385 sprintf(root_device, "%s%d%c", rootdevname,
386 DISKUNIT(rootdev), DISKPART(rootdev) + 'a');
387 }
388
389 return;
390 }
391
392 switch (rootdv->dv_class) {
393 #if defined(NFSCLIENT)
394 case DV_IFNET:
395 strcpy(root_device, "??");
396 mountroot = nfs_mountroot;
397 nfsbootdevname = rootdv->dv_xname;
398 return;
399 #endif
400 #if defined(FFS)
401 case DV_DISK:
402 mountroot = ffs_mountroot;
403 sprintf(root_device, "%s%c", rootdv->dv_xname,
404 DISKPART(rootdev) + 'a');
405 printf("root on %s", root_device);
406 if (nswapdev != NODEV)
407 printf(" swap on %s%c", swapdv->dv_xname,
408 DISKPART(nswapdev) + 'a');
409 printf("\n");
410 break;
411 #endif
412 default:
413 printf("can't figure root, hope your kernel is right\n");
414 return;
415 }
416
417 /*
418 * Make the swap partition on the root drive the primary swap.
419 */
420 temp = NODEV;
421 for (swp = swdevt; swp->sw_dev != NODEV; swp++) {
422 if (major(rootdev) == major(swp->sw_dev) &&
423 DISKUNIT(rootdev) == DISKUNIT(swp->sw_dev)) {
424 temp = swdevt[0].sw_dev;
425 swdevt[0].sw_dev = swp->sw_dev;
426 swp->sw_dev = temp;
427 break;
428 }
429 }
430 if (swp->sw_dev == NODEV)
431 return;
432
433 /*
434 * If dumpdev was the same as the old primary swap device, move
435 * it to the new primary swap device.
436 */
437 if (temp == dumpdev)
438 dumpdev = swdevt[0].sw_dev;
439 }
440
441 static int
442 getstr(cp, size)
443 register char *cp;
444 register int size;
445 {
446 register char *lp;
447 register int c;
448 register int len;
449
450 lp = cp;
451 len = 0;
452 for (;;) {
453 c = cngetc();
454 switch (c) {
455 case '\n':
456 case '\r':
457 printf("\n");
458 *lp++ = '\0';
459 return (len);
460 case '\b':
461 case '\177':
462 case '#':
463 if (len) {
464 --len;
465 --lp;
466 printf("\b \b");
467 }
468 continue;
469 case '@':
470 case 'u'&037:
471 len = 0;
472 lp = cp;
473 printf("\n");
474 continue;
475 default:
476 if (len + 1 >= size || c < ' ') {
477 printf("\007");
478 continue;
479 }
480 printf("%c", c);
481 ++len;
482 *lp++ = c;
483 }
484 }
485 }
486
487 void
488 parse_prom_bootdev()
489 {
490 static char hacked_boot_dev[128];
491 static struct bootdev_data bd;
492 char *cp, *scp, *boot_fields[8];
493 int i, done;
494
495 booted_device = NULL;
496 booted_partition = 0;
497 bootdev_data = NULL;
498
499 prom_getenv(PROM_E_BOOTED_DEV, boot_dev, sizeof(boot_dev));
500 bcopy(boot_dev, hacked_boot_dev, sizeof hacked_boot_dev);
501 #if 0
502 printf("parse_prom_bootdev: boot dev = \"%s\"\n", boot_dev);
503 #endif
504
505 i = 0;
506 scp = cp = hacked_boot_dev;
507 for (done = 0; !done; cp++) {
508 if (*cp != ' ' && *cp != '\0')
509 continue;
510 if (*cp == '\0')
511 done = 1;
512
513 *cp = '\0';
514 boot_fields[i++] = scp;
515 scp = cp + 1;
516 if (i == 8)
517 done = 1;
518 }
519 if (i != 8)
520 return; /* doesn't look like anything we know! */
521
522 #if 0
523 printf("i = %d, done = %d\n", i, done);
524 for (i--; i >= 0; i--)
525 printf("%d = %s\n", i, boot_fields[i]);
526 #endif
527
528 bd.protocol = boot_fields[0];
529 bd.bus = atoi(boot_fields[1]);
530 bd.slot = atoi(boot_fields[2]);
531 bd.channel = atoi(boot_fields[3]);
532 bd.remote_address = boot_fields[4];
533 bd.unit = atoi(boot_fields[5]);
534 bd.boot_dev_type = atoi(boot_fields[6]);
535 bd.ctrl_dev_type = boot_fields[7];
536
537 #if 0
538 printf("parsed: proto = %s, bus = %d, slot = %d, channel = %d,\n",
539 bd.protocol, bd.bus, bd.slot, bd.channel);
540 printf("\tremote = %s, unit = %d, dev_type = %d, ctrl_type = %s\n",
541 bd.remote_address, bd.unit, bd.boot_dev_type, bd.ctrl_dev_type);
542 #endif
543
544 bootdev_data = &bd;
545 }
546
547 int
548 atoi(s)
549 char *s;
550 {
551 int n, neg;
552 char c;
553
554 n = 0;
555 neg = 0;
556
557 while (*s == '-') {
558 s++;
559 neg = !neg;
560 }
561
562 while (*s != '\0') {
563 if (*s < '0' && *s > '9')
564 break;
565
566 n = (10 * n) + (*s - '0');
567 s++;
568 }
569
570 return (neg ? -n : n);
571 }
572
573 void
574 device_register(dev, aux)
575 struct device *dev;
576 void *aux;
577 {
578 extern void (*cpu_device_register) __P((struct device *dev, void *aux));
579
580 if (bootdev_data == NULL) {
581 /*
582 * There is no hope.
583 */
584
585 return;
586 }
587
588 (*cpu_device_register)(dev, aux);
589 }
590