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