autoconf.c revision 46808
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	from: @(#)autoconf.c	7.1 (Berkeley) 5/9/91
37 *	$Id: autoconf.c,v 1.119 1999/05/09 16:45:49 phk Exp $
38 */
39
40/*
41 * Setup the system to run on the current machine.
42 *
43 * Configure() is called at boot time and initializes the vba
44 * device tables and the memory controller monitoring.  Available
45 * devices are determined (from possibilities mentioned in ioconf.c),
46 * and the drivers are initialized.
47 */
48#include "opt_bootp.h"
49#include "opt_ffs.h"
50#include "opt_cd9660.h"
51#include "opt_mfs.h"
52#include "opt_nfsroot.h"
53#include "opt_bus.h"
54#include "opt_rootdevname.h"
55
56#include <sys/param.h>
57#include <sys/systm.h>
58#include <sys/bus.h>
59#include <sys/conf.h>
60#include <sys/disklabel.h>
61#include <sys/diskslice.h>
62#include <sys/reboot.h>
63#include <sys/kernel.h>
64#include <sys/malloc.h>
65#include <sys/mount.h>
66#include <sys/sysctl.h>
67
68#include <machine/bootinfo.h>
69#include <machine/cons.h>
70#include <machine/ipl.h>
71#include <machine/md_var.h>
72#ifdef APIC_IO
73#include <machine/smp.h>
74#endif /* APIC_IO */
75
76#include <i386/isa/icu.h>
77
78#include "isa.h"
79#if NISA > 0
80device_t isa_bus_device = 0;
81#endif
82
83#include "pnp.h"
84#if NPNP > 0
85#include <i386/isa/isa_device.h>
86#include <i386/isa/pnp.h>
87#endif
88
89#include "eisa.h"
90#if NEISA > 0
91#include <i386/eisa/eisaconf.h>
92#endif
93
94#include "pci.h"
95#if NPCI > 0
96#include <pci/pcivar.h>
97#endif
98
99static void	configure_first __P((void *));
100static void	configure __P((void *));
101static void	configure_final __P((void *));
102
103static void	configure_finish __P((void));
104static void	configure_start __P((void));
105static int	setdumpdev __P((dev_t dev));
106static void	setroot __P((void));
107static int	setrootbyname __P((char *name));
108static void	gets __P((char *));
109
110SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL);
111/* SI_ORDER_SECOND is hookable */
112SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL);
113/* SI_ORDER_MIDDLE is hookable */
114SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL);
115
116dev_t	rootdev = NODEV;
117dev_t	dumpdev = NODEV;
118
119#if defined(CD9660) || defined(CD9660_ROOT)
120
121#include <sys/fcntl.h>
122#include <sys/proc.h>
123#include <sys/stat.h>
124#include <machine/clock.h>
125
126/*
127 * XXX All this CD-ROM root stuff is fairly messy.  Ick.
128 *
129 * We need to try out all our potential CDROM drives, so we need a table.
130 */
131static struct {
132	char *name;
133	int major;
134} try_cdrom[] = {
135	{ "cd", 6 },
136	{ "mcd", 7 },
137	{ "scd", 16 },
138	{ "matcd", 17 },
139	{ "wcd", 19 },
140	{ 0, 0}
141};
142
143static int	find_cdrom_root __P((void));
144
145static int
146find_cdrom_root()
147{
148	int i, j, error;
149	struct cdevsw *bd;
150	dev_t orootdev;
151
152#if CD9660_ROOTDELAY > 0
153	DELAY(CD9660_ROOTDELAY * 1000000);
154#endif
155	orootdev = rootdev;
156	for (i = 0 ; i < 2; i++)
157		for (j = 0 ; try_cdrom[j].name ; j++) {
158			if (try_cdrom[j].major >= nblkdev)
159				continue;
160			rootdev = makedev(try_cdrom[j].major, i * 8);
161			bd = bdevsw(rootdev);
162			if (bd == NULL || bd->d_open == NULL)
163				continue;
164			if (bootverbose)
165				printf("trying %s%d as rootdev (0x%x)\n",
166				       try_cdrom[j].name, i, rootdev);
167			error = (bd->d_open)(rootdev, FREAD, S_IFBLK, curproc);
168			if (error == 0) {
169				if (bd->d_close != NULL)
170					(bd->d_close)(rootdev, FREAD, S_IFBLK,
171						      curproc);
172				return 0;
173			}
174		}
175
176	rootdev = orootdev;
177	return EINVAL;
178}
179#endif /* CD9660 || CD9660_ROOT */
180
181#ifdef MFS_ROOT
182extern u_char *mfs_getimage __P((void));
183#endif
184
185static void
186configure_start()
187{
188}
189
190static void
191configure_finish()
192{
193}
194
195device_t nexus_dev;
196
197/*
198 * Determine i/o configuration for a machine.
199 */
200static void
201configure_first(dummy)
202	void *dummy;
203{
204
205	configure_start();		/* DDB hook? */
206}
207
208static void
209configure(dummy)
210	void *dummy;
211{
212
213	/* Allow all routines to decide for themselves if they want intrs */
214	/*
215	 * XXX Since this cannot be achieved on all architectures, we should
216	 * XXX go back to disabling all interrupts until configuration is
217	 * XXX completed and switch any devices that rely on the current
218	 * XXX behavior to no longer rely on interrupts or to register an
219	 * XXX interrupt_driven_config_hook for the task.
220	 */
221	/*
222	 * XXX The above is wrong, because we're implicitly at splhigh(),
223	 * XXX and should stay there, so enabling interrupts in the CPU
224	 * XXX and the ICU at most gives pending interrupts which just get
225	 * XXX in the way.
226	 */
227#ifdef APIC_IO
228	bsp_apic_configure();
229	enable_intr();
230#else
231	enable_intr();
232	INTREN(IRQ_SLAVE);
233#endif /* APIC_IO */
234
235#if NPNP > 0
236	pnp_configure();
237#endif
238
239	/* nexus0 is the top of the i386 device tree */
240	device_add_child(root_bus, "nexus", 0, 0);
241
242	/* initialize new bus architecture */
243	root_bus_configure();
244
245#if NISA > 0
246	if (isa_bus_device)
247		bus_generic_attach(isa_bus_device);
248#endif
249
250	/*
251	 * Now we're ready to handle (pending) interrupts.
252	 * XXX this is slightly misplaced.
253	 */
254	spl0();
255
256	/*
257	 * Allow lowering of the ipl to the lowest kernel level if we
258	 * panic (or call tsleep() before clearing `cold').  No level is
259	 * completely safe (since a panic may occur in a critical region
260	 * at splhigh()), but we want at least bio interrupts to work.
261	 */
262	safepri = cpl;
263}
264
265static void
266configure_final(dummy)
267	void *dummy;
268{
269	int i;
270
271	configure_finish();			/* DDB hook? */
272
273	cninit_finish();
274
275	if (bootverbose) {
276
277#ifdef APIC_IO
278		imen_dump();
279#endif /* APIC_IO */
280
281		/*
282		 * Print out the BIOS's idea of the disk geometries.
283		 */
284		printf("BIOS Geometries:\n");
285		for (i = 0; i < N_BIOS_GEOM; i++) {
286			unsigned long bios_geom;
287			int max_cylinder, max_head, max_sector;
288
289			bios_geom = bootinfo.bi_bios_geom[i];
290
291			/*
292			 * XXX the bootstrap punts a 1200K floppy geometry
293			 * when the get-disk-geometry interrupt fails.  Skip
294			 * drives that have this geometry.
295			 */
296			if (bios_geom == 0x4f010f)
297				continue;
298
299			printf(" %x:%08lx ", i, bios_geom);
300			max_cylinder = bios_geom >> 16;
301			max_head = (bios_geom >> 8) & 0xff;
302			max_sector = bios_geom & 0xff;
303			printf(
304		"0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n",
305			       max_cylinder, max_cylinder + 1,
306			       max_head, max_head + 1,
307			       max_sector, max_sector);
308		}
309		printf(" %d accounted for\n", bootinfo.bi_n_bios_used);
310
311		printf("Device configuration finished.\n");
312	}
313	cold = 0;
314}
315
316
317void
318cpu_rootconf()
319{
320	/*
321	 * XXX NetBSD has a much cleaner approach to finding root.
322	 * XXX We should adopt their code.
323	 */
324#if defined(CD9660) || defined(CD9660_ROOT)
325	if ((boothowto & RB_CDROM)) {
326		if (bootverbose)
327			printf("Considering CD-ROM root f/s.\n");
328		/* NB: find_cdrom_root() sets rootdev if successful. */
329		if (find_cdrom_root() == 0)
330			mountrootfsname = "cd9660";
331		else if (bootverbose)
332			printf("No CD-ROM available as root f/s.\n");
333	}
334#endif
335
336#ifdef MFS_ROOT
337	if (!mountrootfsname) {
338		if (bootverbose)
339			printf("Considering MFS root f/s.\n");
340		if (mfs_getimage())
341			mountrootfsname = "mfs";
342		else if (bootverbose)
343			printf("No MFS image available as root f/s.\n");
344	}
345#endif
346
347#ifdef BOOTP_NFSROOT
348	if (!mountrootfsname && !nfs_diskless_valid) {
349		if (bootverbose)
350			printf("Considering BOOTP NFS root f/s.\n");
351		mountrootfsname = "nfs";
352	}
353#endif /* BOOTP_NFSROOT */
354#if defined(NFS) || defined(NFS_ROOT)
355	if (!mountrootfsname && nfs_diskless_valid) {
356		if (bootverbose)
357			printf("Considering NFS root f/s.\n");
358		mountrootfsname = "nfs";
359	}
360#endif /* NFS */
361
362#if defined(FFS) || defined(FFS_ROOT)
363	if (!mountrootfsname) {
364		mountrootfsname = "ufs";
365		if (bootverbose)
366			printf("Considering FFS root f/s.\n");
367		if (boothowto & RB_ASKNAME)
368			setconf();
369		else
370			setroot();
371	}
372#endif
373
374	if (!mountrootfsname) {
375		panic("Nobody wants to mount my root for me");
376	}
377}
378
379
380void
381cpu_dumpconf()
382{
383	if (setdumpdev(dumpdev) != 0)
384		dumpdev = NODEV;
385}
386
387static int
388setdumpdev(dev)
389	dev_t dev;
390{
391	int maj, psize;
392	long newdumplo;
393
394	if (dev == NODEV) {
395		dumpdev = dev;
396		return (0);
397	}
398	maj = major(dev);
399	if (maj >= nblkdev || bdevsw(dev) == NULL)
400		return (ENXIO);		/* XXX is this right? */
401	if (bdevsw(dev)->d_psize == NULL)
402		return (ENXIO);		/* XXX should be ENODEV ? */
403	psize = bdevsw(dev)->d_psize(dev);
404	if (psize == -1)
405		return (ENXIO);		/* XXX should be ENODEV ? */
406	/*
407	 * XXX should clean up checking in dumpsys() to be more like this,
408	 * and nuke dodump sysctl (too many knobs), and move this to
409	 * kern_shutdown.c...
410	 */
411	newdumplo = psize - Maxmem * PAGE_SIZE / DEV_BSIZE;
412	if (newdumplo < 0)
413		return (ENOSPC);
414	dumpdev = dev;
415	dumplo = newdumplo;
416	return (0);
417}
418
419
420u_long	bootdev = 0;		/* not a dev_t - encoding is different */
421
422#define FDMAJOR 2
423#define FDUNITSHIFT     6
424
425/*
426 * Attempt to find the device from which we were booted.
427 * If we can do so, and not instructed not to do so,
428 * set rootdevs[] and rootdevnames[] to correspond to the
429 * boot device(s).
430 */
431static void
432setroot()
433{
434	int majdev, mindev, unit, slice, part;
435	dev_t newrootdev, dev;
436	char partname[2];
437	char *sname;
438
439	if (boothowto & RB_DFLTROOT) {
440#ifdef ROOTDEVNAME
441		setrootbyname(ROOTDEVNAME);
442#else
443		setconf();
444#endif
445		return;
446	}
447	if ((bootdev & B_MAGICMASK) != B_DEVMAGIC)
448		return;
449	majdev = B_TYPE(bootdev);
450	dev = makedev(majdev, 0);
451	if (majdev >= nblkdev || bdevsw(dev) == NULL)
452		return;
453	unit = B_UNIT(bootdev);
454	slice = B_SLICE(bootdev);
455	if (slice == WHOLE_DISK_SLICE)
456		slice = COMPATIBILITY_SLICE;
457	if (slice < 0 || slice >= MAX_SLICES)
458		return;
459
460	/*
461	 * XXX kludge for inconsistent unit numbering and lack of slice
462	 * support for floppies.
463	 */
464	if (majdev == FDMAJOR) {
465		slice = COMPATIBILITY_SLICE;
466		part = RAW_PART;
467		mindev = unit << FDUNITSHIFT;
468	} else {
469		part = B_PARTITION(bootdev);
470		mindev = dkmakeminor(unit, slice, part);
471	}
472
473	newrootdev = makedev(majdev, mindev);
474	rootdevs[0] = newrootdev;
475	sname = dsname(bdevsw(newrootdev)->d_name, unit, slice, part, partname);
476	rootdevnames[0] = malloc(strlen(sname) + 2, M_DEVBUF, M_NOWAIT);
477	sprintf(rootdevnames[0], "%s%s", sname, partname);
478
479	/*
480	 * For properly dangerously dedicated disks (ones with a historical
481	 * bogus partition table), the boot blocks will give slice = 4, but
482	 * the kernel will only provide the compatibility slice since it
483	 * knows that slice 4 is not a real slice.  Arrange to try mounting
484	 * the compatibility slice as root if mounting the slice passed by
485	 * the boot blocks fails.  This handles the dangerously dedicated
486	 * case and perhaps others.
487	 */
488	if (slice == COMPATIBILITY_SLICE)
489		return;
490	slice = COMPATIBILITY_SLICE;
491	rootdevs[1] = dkmodslice(newrootdev, slice);
492	sname = dsname(bdevsw(newrootdev)->d_name, unit, slice, part, partname);
493	rootdevnames[1] = malloc(strlen(sname) + 2, M_DEVBUF, M_NOWAIT);
494	sprintf(rootdevnames[1], "%s%s", sname, partname);
495}
496
497
498static int
499sysctl_kern_dumpdev SYSCTL_HANDLER_ARGS
500{
501	int error;
502	dev_t ndumpdev;
503
504	ndumpdev = dumpdev;
505	error = sysctl_handle_opaque(oidp, &ndumpdev, sizeof ndumpdev, req);
506	if (error == 0 && req->newptr != NULL)
507		error = setdumpdev(ndumpdev);
508	return (error);
509}
510
511SYSCTL_PROC(_kern, KERN_DUMPDEV, dumpdev, CTLTYPE_OPAQUE|CTLFLAG_RW,
512	0, sizeof dumpdev, sysctl_kern_dumpdev, "T,dev_t", "");
513
514
515
516static int
517setrootbyname(char *name)
518{
519	char *cp;
520	int bd, unit, slice, part;
521	dev_t dev;
522
523	slice = 0;
524	part = 0;
525	cp = name;
526	while (cp != '\0' && (*cp < '0' || *cp > '9'))
527		cp++;
528	if (cp == name) {
529		printf("missing device name\n");
530		return(1);
531	}
532	if (*cp == '\0') {
533		printf("missing unit number\n");
534		return(1);
535	}
536	unit = *cp - '0';
537	*cp++ = '\0';
538	for (bd = 0; bd < nblkdev; bd++) {
539		dev = makedev(bd, 0);
540		if (bdevsw(dev) != NULL &&
541		    strcmp(bdevsw(dev)->d_name, name) == 0)
542			goto gotit;
543	}
544	return (2);
545gotit:
546	while (*cp >= '0' && *cp <= '9')
547		unit += 10 * unit + *cp++ - '0';
548	if (*cp == 's' && cp[1] >= '0' && cp[1] <= '9') {
549		slice = cp[1] - '0';
550		cp += 2;
551	}
552	if (*cp >= 'a' && *cp <= 'h') {
553		part = *cp - 'a';
554		cp++;
555	}
556	if (*cp != '\0') {
557		printf("junk after name\n");
558		return (1);
559	}
560	printf("driver=%s, unit=%d, slice=%d, part=%d\n",
561		name, unit, slice, part);
562	rootdev = makedev(bd, dkmakeminor(unit, slice, part));
563	return 0;
564}
565
566void
567setconf()
568{
569	char name[128];
570	int i;
571	dev_t dev;
572
573	for(;;) {
574		printf("root device? ");
575		gets(name);
576		i = setrootbyname(name);
577		if (!i)
578			return;
579
580		printf("use one of:\n");
581		for (i = 0; i < nblkdev; i++) {
582			dev = makedev(i, 0);
583			if (bdevsw(dev) != NULL)
584			    printf(" %s", bdevsw(dev)->d_name);
585		}
586		printf(" followed by a unit number...\n");
587	}
588}
589
590static void
591gets(cp)
592	char *cp;
593{
594	register char *lp;
595	register int c;
596
597	lp = cp;
598	for (;;) {
599		printf("%c", c = cngetc() & 0177);
600		switch (c) {
601		case -1:
602		case '\n':
603		case '\r':
604			*lp++ = '\0';
605			return;
606		case '\b':
607		case '\177':
608			if (lp > cp) {
609				printf(" \b");
610				lp--;
611			}
612			continue;
613		case '#':
614			lp--;
615			if (lp < cp)
616				lp = cp;
617			continue;
618		case '@':
619		case 'u' & 037:
620			lp = cp;
621			printf("%c", '\n');
622			continue;
623		default:
624			*lp++ = c;
625		}
626	}
627}
628