xref: /netbsd/sys/arch/i386/i386/autoconf.c (revision bf9ec67e)
1 /*	$NetBSD: autoconf.c,v 1.60 2002/01/07 21:47:00 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1990 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * William Jolitz.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	@(#)autoconf.c	7.1 (Berkeley) 5/9/91
39  */
40 
41 /*
42  * Setup the system to run on the current machine.
43  *
44  * Configure() is called at boot time and initializes the vba
45  * device tables and the memory controller monitoring.  Available
46  * devices are determined (from possibilities mentioned in ioconf.c),
47  * and the drivers are initialized.
48  */
49 
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.60 2002/01/07 21:47:00 thorpej Exp $");
52 
53 #include "opt_compat_oldboot.h"
54 
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/buf.h>
58 #include <sys/dkstat.h>
59 #include <sys/disklabel.h>
60 #include <sys/conf.h>
61 #ifdef COMPAT_OLDBOOT
62 #include <sys/reboot.h>
63 #endif
64 #include <sys/device.h>
65 #include <sys/malloc.h>
66 #include <sys/vnode.h>
67 #include <sys/fcntl.h>
68 #include <sys/dkio.h>
69 
70 #include <machine/pte.h>
71 #include <machine/cpu.h>
72 #include <machine/bootinfo.h>
73 
74 static int match_harddisk __P((struct device *, struct btinfo_bootdisk *));
75 static void matchbiosdisks __P((void));
76 static void findroot __P((void));
77 static int is_valid_disk __P((struct device *));
78 
79 extern struct disklist *i386_alldisks;
80 extern int i386_ndisks;
81 
82 #include "bios32.h"
83 #if NBIOS32 > 0
84 #include <machine/bios32.h>
85 #endif
86 
87 #include "opt_pcibios.h"
88 #ifdef PCIBIOS
89 #include <dev/pci/pcireg.h>
90 #include <dev/pci/pcivar.h>
91 #include <i386/pci/pcibios.h>
92 #endif
93 
94 struct device *booted_device;
95 int booted_partition;
96 
97 /*
98  * Determine i/o configuration for a machine.
99  */
100 void
101 cpu_configure()
102 {
103 
104 	startrtclock();
105 
106 #if NBIOS32 > 0
107 	bios32_init();
108 #endif
109 #ifdef PCIBIOS
110 	pcibios_init();
111 #endif
112 
113 	if (config_rootfound("mainbus", NULL) == NULL)
114 		panic("configure: mainbus not configured");
115 
116 	printf("biomask %x netmask %x ttymask %x\n",
117 	    (u_short)imask[IPL_BIO], (u_short)imask[IPL_NET],
118 	    (u_short)imask[IPL_TTY]);
119 
120 	spl0();
121 
122 	/* Set up proc0's TSS and LDT (after the FPU is configured). */
123 	i386_proc0_tss_ldt_init();
124 
125 	/* XXX Finish deferred buffer cache allocation. */
126 	i386_bufinit();
127 }
128 
129 void
130 cpu_rootconf()
131 {
132 	findroot();
133 	matchbiosdisks();
134 
135 	printf("boot device: %s\n",
136 	    booted_device ? booted_device->dv_xname : "<unknown>");
137 
138 	setroot(booted_device, booted_partition);
139 }
140 
141 /*
142  * XXX ugly bit of code. But, this is the only safe time that the
143  * match between BIOS disks and native disks can be done.
144  */
145 static void
146 matchbiosdisks()
147 {
148 	struct btinfo_biosgeom *big;
149 	struct bi_biosgeom_entry *be;
150 	struct device *dv;
151 	struct devnametobdevmaj *d;
152 	int i, ck, error, m, n;
153 	struct vnode *tv;
154 	char mbr[DEV_BSIZE];
155 	int  dklist_size;
156 
157 	big = lookup_bootinfo(BTINFO_BIOSGEOM);
158 
159 	if (big == NULL)
160 		return;
161 
162 	/*
163 	 * First, count all native disks
164 	 */
165 	for (dv = alldevs.tqh_first; dv != NULL; dv = dv->dv_list.tqe_next)
166 		if (is_valid_disk(dv))
167 			i386_ndisks++;
168 
169 	if (i386_ndisks == 0)
170 		return;
171 
172 	dklist_size = sizeof (struct disklist) + (i386_ndisks - 1) *
173 	    sizeof (struct nativedisk_info);
174 
175 	/* XXX M_TEMP is wrong */
176 	i386_alldisks = malloc(dklist_size, M_TEMP, M_NOWAIT);
177 	if (i386_alldisks == NULL)
178 		return;
179 
180 	memset(i386_alldisks, 0, dklist_size);
181 
182 	i386_alldisks->dl_nnativedisks = i386_ndisks;
183 	i386_alldisks->dl_nbiosdisks = big->num;
184 	for (i = 0; i < big->num; i++) {
185 		i386_alldisks->dl_biosdisks[i].bi_dev = big->disk[i].dev;
186 		i386_alldisks->dl_biosdisks[i].bi_sec = big->disk[i].sec;
187 		i386_alldisks->dl_biosdisks[i].bi_head = big->disk[i].head;
188 		i386_alldisks->dl_biosdisks[i].bi_cyl = big->disk[i].cyl;
189 		i386_alldisks->dl_biosdisks[i].bi_lbasecs = big->disk[i].totsec;
190 		i386_alldisks->dl_biosdisks[i].bi_flags = big->disk[i].flags;
191 	}
192 
193 	/*
194 	 * XXX code duplication from findroot()
195 	 */
196 	n = -1;
197 	for (dv = alldevs.tqh_first; dv != NULL; dv = dv->dv_list.tqe_next) {
198 		if (dv->dv_class != DV_DISK)
199 			continue;
200 #ifdef GEOM_DEBUG
201 		printf("matchbiosdisks: trying to match (%s) %s\n",
202 		    dv->dv_xname, dv->dv_cfdata->cf_driver->cd_name);
203 #endif
204 		if (is_valid_disk(dv)) {
205 			n++;
206 			sprintf(i386_alldisks->dl_nativedisks[n].ni_devname,
207 			    "%s%d", dv->dv_cfdata->cf_driver->cd_name,
208 			    dv->dv_unit);
209 
210 			for (d = dev_name2blk; d->d_name &&
211 			   strcmp(d->d_name, dv->dv_cfdata->cf_driver->cd_name);
212 			   d++);
213 			if (d->d_name == NULL)
214 				return;
215 
216 			if (bdevvp(MAKEDISKDEV(d->d_maj, dv->dv_unit, RAW_PART),
217 			    &tv))
218 				panic("matchbiosdisks: can't alloc vnode");
219 
220 			error = VOP_OPEN(tv, FREAD, NOCRED, 0);
221 			if (error) {
222 				vput(tv);
223 				continue;
224 			}
225 			error = vn_rdwr(UIO_READ, tv, mbr, DEV_BSIZE, 0,
226 			    UIO_SYSSPACE, 0, NOCRED, NULL, 0);
227 			VOP_CLOSE(tv, FREAD, NOCRED, 0);
228 			if (error) {
229 #ifdef GEOM_DEBUG
230 				printf("matchbiosdisks: %s: MBR read failure\n",
231 				    dv->dv_xname);
232 #endif
233 				continue;
234 			}
235 
236 			for (ck = i = 0; i < DEV_BSIZE; i++)
237 				ck += mbr[i];
238 			for (m = i = 0; i < big->num; i++) {
239 				be = &big->disk[i];
240 #ifdef GEOM_DEBUG
241 				printf("match %s with %d\n", dv->dv_xname, i);
242 				printf("dev ck %x bios ck %x\n", ck, be->cksum);
243 #endif
244 				if (be->flags & BI_GEOM_INVALID)
245 					continue;
246 				if (be->cksum == ck &&
247 				    !memcmp(&mbr[MBR_PARTOFF], be->dosparts,
248 					NMBRPART *
249 					    sizeof (struct mbr_partition))) {
250 #ifdef GEOM_DEBUG
251 					printf("matched bios disk %x with %s\n",
252 					    be->dev, be->devname);
253 #endif
254 					i386_alldisks->dl_nativedisks[n].
255 					    ni_biosmatches[m++] = i;
256 				}
257 			}
258 			i386_alldisks->dl_nativedisks[n].ni_nmatches = m;
259 			vput(tv);
260 		}
261 	}
262 }
263 
264 #ifdef COMPAT_OLDBOOT
265 u_long	bootdev = 0;		/* should be dev_t, but not until 32 bits */
266 #endif
267 
268 /*
269  * helper function for "findroot()":
270  * return nonzero if disk device matches bootinfo
271  */
272 static int
273 match_harddisk(dv, bid)
274 	struct device *dv;
275 	struct btinfo_bootdisk *bid;
276 {
277 	struct devnametobdevmaj *i;
278 	struct vnode *tmpvn;
279 	int error;
280 	struct disklabel label;
281 	int found = 0;
282 
283 	/*
284 	 * A disklabel is required here.  The
285 	 * bootblocks don't refuse to boot from
286 	 * a disk without a label, but this is
287 	 * normally not wanted.
288 	 */
289 	if (bid->labelsector == -1)
290 		return(0);
291 
292 	/*
293 	 * lookup major number for disk block device
294 	 */
295 	i = dev_name2blk;
296 	while (i->d_name &&
297 	       strcmp(i->d_name, dv->dv_cfdata->cf_driver->cd_name))
298 		i++;
299 	if (i->d_name == NULL)
300 		return(0); /* XXX panic() ??? */
301 
302 	/*
303 	 * Fake a temporary vnode for the disk, open
304 	 * it, and read the disklabel for comparison.
305 	 */
306 	if (bdevvp(MAKEDISKDEV(i->d_maj, dv->dv_unit, bid->partition), &tmpvn))
307 		panic("findroot can't alloc vnode");
308 	error = VOP_OPEN(tmpvn, FREAD, NOCRED, 0);
309 	if (error) {
310 #ifndef DEBUG
311 		/*
312 		 * Ignore errors caused by missing
313 		 * device, partition or medium.
314 		 */
315 		if (error != ENXIO && error != ENODEV)
316 #endif
317 			printf("findroot: can't open dev %s%c (%d)\n",
318 			       dv->dv_xname, 'a' + bid->partition, error);
319 		vput(tmpvn);
320 		return(0);
321 	}
322 	error = VOP_IOCTL(tmpvn, DIOCGDINFO, (caddr_t)&label, FREAD, NOCRED, 0);
323 	if (error) {
324 		/*
325 		 * XXX can't happen - open() would
326 		 * have errored out (or faked up one)
327 		 */
328 		printf("can't get label for dev %s%c (%d)\n",
329 		       dv->dv_xname, 'a' + bid->partition, error);
330 		goto closeout;
331 	}
332 
333 	/* compare with our data */
334 	if (label.d_type == bid->label.type &&
335 	    label.d_checksum == bid->label.checksum &&
336 	    !strncmp(label.d_packname, bid->label.packname, 16))
337 		found = 1;
338 
339 closeout:
340 	VOP_CLOSE(tmpvn, FREAD, NOCRED, 0);
341 	vput(tmpvn);
342 	return(found);
343 }
344 
345 /*
346  * Attempt to find the device from which we were booted.
347  * If we can do so, and not instructed not to do so,
348  * change rootdev to correspond to the load device.
349  */
350 void
351 findroot(void)
352 {
353 	struct btinfo_bootdisk *bid;
354 	struct device *dv;
355 #ifdef COMPAT_OLDBOOT
356 	int i, majdev, unit, part;
357 	char buf[32];
358 #endif
359 
360 	if (booted_device)
361 		return;
362 
363 	if (lookup_bootinfo(BTINFO_NETIF)) {
364 		/*
365 		 * We got netboot interface information, but
366 		 * "device_register()" couldn't match it to a configured
367 		 * device. Bootdisk information cannot be present at the
368 		 * same time, so give up.
369 		 */
370 		printf("findroot: netboot interface not found\n");
371 		return;
372 	}
373 
374 	bid = lookup_bootinfo(BTINFO_BOOTDISK);
375 	if (bid) {
376 		/*
377 		 * Scan all disk devices for ones that match the passed data.
378 		 * Don't break if one is found, to get possible multiple
379 		 * matches - for problem tracking. Use the first match anyway
380 		 * because lower device numbers are more likely to be the
381 		 * boot device.
382 		 */
383 		for (dv = alldevs.tqh_first; dv != NULL;
384 		dv = dv->dv_list.tqe_next) {
385 			if (dv->dv_class != DV_DISK)
386 				continue;
387 
388 			if (!strcmp(dv->dv_cfdata->cf_driver->cd_name, "fd")) {
389 				/*
390 				 * Assume the configured unit number matches
391 				 * the BIOS device number.  (This is the old
392 				 * behaviour.)  Needs some ideas how to handle
393 				 * BIOS's "swap floppy drive" options.
394 				 */
395 				if ((bid->biosdev & 0x80) ||
396 				    dv->dv_unit != bid->biosdev)
397 					continue;
398 
399 				goto found;
400 			}
401 
402 			if (is_valid_disk(dv)) {
403 				/*
404 				 * Don't trust BIOS device numbers, try
405 				 * to match the information passed by the
406 				 * bootloader instead.
407 				 */
408 				if ((bid->biosdev & 0x80) == 0 ||
409 				    !match_harddisk(dv, bid))
410 					continue;
411 
412 				goto found;
413 			}
414 
415 			/* no "fd", "wd", "sd", "ld", "ed" */
416 			continue;
417 
418 found:
419 			if (booted_device) {
420 				printf("warning: double match for boot "
421 				    "device (%s, %s)\n", booted_device->dv_xname,
422 				    dv->dv_xname);
423 				continue;
424 			}
425 			booted_device = dv;
426 			booted_partition = bid->partition;
427 		}
428 
429 		if (booted_device)
430 			return;
431 	}
432 
433 #ifdef COMPAT_OLDBOOT
434 #if 0
435 	printf("howto %x bootdev %x ", boothowto, bootdev);
436 #endif
437 
438 	if ((bootdev & B_MAGICMASK) != (u_long)B_DEVMAGIC)
439 		return;
440 
441 	majdev = (bootdev >> B_TYPESHIFT) & B_TYPEMASK;
442 	for (i = 0; dev_name2blk[i].d_name != NULL; i++)
443 		if (majdev == dev_name2blk[i].d_maj)
444 			break;
445 	if (dev_name2blk[i].d_name == NULL)
446 		return;
447 
448 	part = (bootdev >> B_PARTITIONSHIFT) & B_PARTITIONMASK;
449 	unit = (bootdev >> B_UNITSHIFT) & B_UNITMASK;
450 
451 	sprintf(buf, "%s%d", dev_name2blk[i].d_name, unit);
452 	for (dv = alldevs.tqh_first; dv != NULL;
453 	    dv = dv->dv_list.tqe_next) {
454 		if (strcmp(buf, dv->dv_xname) == 0) {
455 			booted_device = dv;
456 			booted_partition = part;
457 			return;
458 		}
459 	}
460 #endif
461 }
462 
463 #include "pci.h"
464 
465 #include <dev/isa/isavar.h>
466 #if NPCI > 0
467 #include <dev/pci/pcivar.h>
468 #endif
469 
470 void
471 device_register(dev, aux)
472 	struct device *dev;
473 	void *aux;
474 {
475 	/*
476 	 * Handle network interfaces here, the attachment information is
477 	 * not available driver independantly later.
478 	 * For disks, there is nothing useful available at attach time.
479 	 */
480 	if (dev->dv_class == DV_IFNET) {
481 		struct btinfo_netif *bin = lookup_bootinfo(BTINFO_NETIF);
482 		if (bin == NULL)
483 			return;
484 
485 		/*
486 		 * We don't check the driver name against the device name
487 		 * passed by the boot ROM. The ROM should stay usable
488 		 * if the driver gets obsoleted.
489 		 * The physical attachment information (checked below)
490 		 * must be sufficient to identify the device.
491 		 */
492 
493 		if (bin->bus == BI_BUS_ISA &&
494 		    !strcmp(dev->dv_parent->dv_cfdata->cf_driver->cd_name,
495 		    "isa")) {
496 			struct isa_attach_args *iaa = aux;
497 
498 			/* compare IO base address */
499 			/* XXXJRT what about multiple I/O addrs? */
500 			if (iaa->ia_nio > 0 &&
501 			    bin->addr.iobase == iaa->ia_io[0].ir_addr)
502 				goto found;
503 		}
504 #if NPCI > 0
505 		if (bin->bus == BI_BUS_PCI &&
506 		    !strcmp(dev->dv_parent->dv_cfdata->cf_driver->cd_name,
507 		    "pci")) {
508 			struct pci_attach_args *paa = aux;
509 			int b, d, f;
510 
511 			/*
512 			 * Calculate BIOS representation of:
513 			 *
514 			 *	<bus,device,function>
515 			 *
516 			 * and compare.
517 			 */
518 			pci_decompose_tag(paa->pa_pc, paa->pa_tag, &b, &d, &f);
519 			if (bin->addr.tag == ((b << 8) | (d << 3) | f))
520 				goto found;
521 		}
522 #endif
523 	}
524 	return;
525 
526 found:
527 	if (booted_device) {
528 		/* XXX should be a "panic()" */
529 		printf("warning: double match for boot device (%s, %s)\n",
530 		    booted_device->dv_xname, dev->dv_xname);
531 		return;
532 	}
533 	booted_device = dev;
534 }
535 
536 static int
537 is_valid_disk(struct device *dv)
538 {
539 	const char *name;
540 
541 	if (dv->dv_class != DV_DISK)
542 		return (0);
543 
544 	name = dv->dv_cfdata->cf_driver->cd_name;
545 
546 	return (strcmp(name, "sd") == 0 || strcmp(name, "wd") == 0 ||
547 	    strcmp(name, "ld") == 0 || strcmp(name, "ed") == 0);
548 }
549