/* $OpenBSD: autoconf.c,v 1.43 2007/06/01 19:25:09 deraadt Exp $ */ /* $NetBSD: autoconf.c,v 1.45 1999/04/10 17:31:02 kleink Exp $ */ /* * Copyright (c) 1996 Jason R. Thorpe. All rights reserved. * Copyright (c) 1988 University of Utah. * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah $Hdr: autoconf.c 1.36 92/12/20$ * * @(#)autoconf.c 8.2 (Berkeley) 1/12/94 */ /* * Setup the system to run on the current machine. * * cpu_configure() is called at boot time. Available * devices are determined (from possibilities mentioned in ioconf.c), * and the drivers are initialized. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The following several variables are related to * the configuration process, and are used in initializing * the machine. */ struct extent *extio; extern caddr_t internalhpib; extern char *extiobase; /* The boot device. */ struct device *bootdv; /* The device we mount as root. */ struct device *root_device; /* How we were booted. */ u_int bootdev; /* * This information is built during the autoconfig process. * A little explanation about the way this works is in order. * * device_register() links all devices into dev_data_list. * If the device is an hpib controller, it is also linked * into dev_data_list_hpib. If the device is a scsi controller, * it is also linked into dev_data_list_scsi. * * dev_data_list_hpib and dev_data_list_scsi are sorted * by select code, from lowest to highest. * * After autoconfiguration is complete, we need to determine * which device was the boot device. The boot block assigns * controller unit numbers in order of select code. Thus, * providing the controller is configured in the kernel, we * can determine our version of controller unit number from * the sorted hpib/scsi list. * * At this point, we know the controller (device type * encoded in bootdev tells us "scsi disk", or "hpib tape", * etc.). The next step is to find the device which * has the following properties: * * - A child of the boot controller. * - Same slave as encoded in bootdev. * - Same physical unit as encoded in bootdev. * * Later, after we've set the root device in stone, we * reverse the process to re-encode bootdev so it can be * passed back to the boot block. */ struct dev_data { LIST_ENTRY(dev_data) dd_list; /* dev_data_list */ LIST_ENTRY(dev_data) dd_clist; /* ctlr list */ struct device *dd_dev; /* device described by this entry */ int dd_scode; /* select code of device */ int dd_slave; /* ...or slave */ int dd_punit; /* and punit... */ }; typedef LIST_HEAD(, dev_data) ddlist_t; ddlist_t dev_data_list; /* all dev_datas */ ddlist_t dev_data_list_hpib; /* hpib controller dev_datas */ ddlist_t dev_data_list_scsi; /* scsi controller dev_datas */ void findbootdev(void); void findbootdev_slave(ddlist_t *, int, int, int); void setbootdev(void); static struct dev_data *dev_data_lookup(struct device *); static void dev_data_insert(struct dev_data *, ddlist_t *); static int device_match(const char *, const char *); int mainbusmatch(struct device *, void *, void *); void mainbusattach(struct device *, struct device *, void *); int mainbussearch(struct device *, void *, void *); struct cfattach mainbus_ca = { sizeof(struct device), mainbusmatch, mainbusattach }; struct cfdriver mainbus_cd = { NULL, "mainbus", DV_DULL }; int mainbusmatch(parent, match, aux) struct device *parent; void *match, *aux; { static int mainbus_matched = 0; /* Allow only one instance. */ if (mainbus_matched) return (0); mainbus_matched = 1; return (1); } void mainbusattach(parent, self, aux) struct device *parent, *self; void *aux; { printf("\n"); /* Search for and attach children. */ config_search(mainbussearch, self, NULL); } int mainbussearch(parent, match, aux) struct device *parent; void *match, *aux; { struct cfdata *cf = match; if ((*cf->cf_attach->ca_match)(parent, cf, NULL) > 0) config_attach(parent, cf, NULL, NULL); return (0); } /* * Determine the device configuration for the running system. */ void cpu_configure() { /* * Initialize the dev_data_lists. */ LIST_INIT(&dev_data_list); LIST_INIT(&dev_data_list_hpib); LIST_INIT(&dev_data_list_scsi); (void)splhigh(); if (config_rootfound("mainbus", "mainbus") == NULL) panic("no mainbus found"); (void)spl0(); intr_printlevels(); /* * Find boot device. */ if ((bootdev & B_MAGICMASK) != B_DEVMAGIC) { printf("WARNING: boot program didn't supply boot device.\n"); printf("Please update your boot program.\n"); } else { findbootdev(); if (bootdv == NULL) { printf("WARNING: can't find match for bootdev:\n"); printf( "type = %d, ctlr = %d, slave = %d, punit = %d, part = %d\n", B_TYPE(bootdev), B_ADAPTOR(bootdev), B_CONTROLLER(bootdev), B_UNIT(bootdev), B_PARTITION(bootdev)); bootdev = 0; /* invalidate bootdev */ } else { printf("boot device: %s\n", bootdv->dv_xname); } } cold = 0; } void diskconf(void) { int bootpartition = 0; /* * If bootdev is bogus, ask the user anyhow. */ if (bootdev == 0) boothowto |= RB_ASKNAME; else bootpartition = B_PARTITION(bootdev); /* * If we booted from tape, ask the user. */ if (bootdv != NULL && bootdv->dv_class == DV_TAPE) boothowto |= RB_ASKNAME; setroot(bootdv, bootpartition, RB_USERREQ); dumpconf(); /* * Set bootdev based on the device we booted from. * This is given to the boot program when we reboot. */ setbootdev(); } /********************************************************************** * Code to find and set the boot device **********************************************************************/ static int device_match(const char *dvname, const char *template) { size_t len = strlen(template); char unit; if (strncmp(dvname, template, len) != 0) return (1); /* Check that we are immediately followed by an unit number. */ unit = dvname[len]; if (unit < '0' || unit > '9') return (1); return (0); } /* * Register a device. We're passed the device and the arguments * used to attach it. This is used to find the boot device. */ void device_register(dev, aux) struct device *dev; void *aux; { struct dev_data *dd; static int seen_netdevice = 0; /* * Allocate a dev_data structure and fill it in. * This means making some tests twice, but we don't * care; this doesn't really have to be fast. * * Note that we only really care about devices that * we can mount as root. */ dd = (struct dev_data *)malloc(sizeof(struct dev_data), M_DEVBUF, M_NOWAIT); if (dd == NULL) panic("device_register: can't allocate dev_data"); bzero(dd, sizeof(struct dev_data)); dd->dd_dev = dev; /* * BOOTROM and boot program can really only understand * using the lowest select code network interface, * so we ignore all but the first. */ if (dev->dv_class == DV_IFNET && seen_netdevice == 0) { struct dio_attach_args *da = aux; seen_netdevice = 1; dd->dd_scode = da->da_scode; goto linkup; } if (device_match(dev->dv_xname, "fhpib") == 0 || device_match(dev->dv_xname, "nhpib") == 0 || device_match(dev->dv_xname, "spc") == 0) { struct dio_attach_args *da = aux; dd->dd_scode = da->da_scode; goto linkup; } if (device_match(dev->dv_xname, "hd") == 0) { struct hpibbus_attach_args *ha = aux; dd->dd_slave = ha->ha_slave; dd->dd_punit = ha->ha_punit; goto linkup; } if (device_match(dev->dv_xname, "cd") == 0 || device_match(dev->dv_xname, "sd") == 0 || device_match(dev->dv_xname, "st") == 0) { struct scsi_attach_args *sa = aux; dd->dd_slave = sa->sa_sc_link->target; dd->dd_punit = sa->sa_sc_link->lun; goto linkup; } /* * Didn't need the dev_data. */ free(dd, M_DEVBUF); return; linkup: LIST_INSERT_HEAD(&dev_data_list, dd, dd_list); if (device_match(dev->dv_xname, "fhpib") == 0 || device_match(dev->dv_xname, "nhpib") == 0) { dev_data_insert(dd, &dev_data_list_hpib); return; } if (device_match(dev->dv_xname, "spc") == 0) { dev_data_insert(dd, &dev_data_list_scsi); return; } } void findbootdev() { int type, ctlr, slave, punit; int scsiboot, hpibboot, netboot; struct dev_data *dd; bootdv = NULL; if ((bootdev & B_MAGICMASK) != B_DEVMAGIC) return; type = B_TYPE(bootdev); ctlr = B_ADAPTOR(bootdev); slave = B_CONTROLLER(bootdev); punit = B_UNIT(bootdev); scsiboot = (type == 4); /* sd major */ hpibboot = (type == 0 || type == 2); /* ct/hd major */ netboot = (type == 6); /* le - special */ /* * Check for network boot first, since it's a little * different. The BOOTROM/boot program can only boot * off of the first (lowest select code) ethernet * device. device_register() knows this and only * registers one DV_IFNET. This is a safe assumption * since the code that finds devices on the DIO bus * always starts at scode 0 and works its way up. */ if (netboot) { LIST_FOREACH(dd, &dev_data_list, dd_list) { if (dd->dd_dev->dv_class == DV_IFNET) { /* * Found it! */ bootdv = dd->dd_dev; break; } } return; } /* * Check for HP-IB boots next. */ if (hpibboot) { findbootdev_slave(&dev_data_list_hpib, ctlr, slave, punit); if (bootdv == NULL) return; #ifdef DIAGNOSTIC /* * Sanity check. */ if ((type == 0 && device_match(bootdv->dv_xname, "ct")) || (type == 2 && device_match(bootdv->dv_xname, "hd"))) { printf("WARNING: boot device/type mismatch!\n"); printf("device = %s, type = %d\n", bootdv->dv_xname, type); bootdv = NULL; } #endif return; } /* * Check for SCSI boots last. */ if (scsiboot) { findbootdev_slave(&dev_data_list_scsi, ctlr, slave, punit); if (bootdv == NULL) return; #ifdef DIAGNOSTIC /* * Sanity check. */ if (device_match(bootdv->dv_xname, "cd") != 0 && device_match(bootdv->dv_xname, "sd") != 0 && device_match(bootdv->dv_xname, "st") != 0) { printf("WARNING: boot device/type mismatch!\n"); printf("device = %s, type = %d\n", bootdv->dv_xname, type); bootdv = NULL; } #endif return; } /* Oof! */ printf("WARNING: UNKNOWN BOOT DEVICE TYPE = %d\n", type); } void findbootdev_slave(ddlist, ctlr, slave, punit) ddlist_t *ddlist; int ctlr, slave, punit; { struct dev_data *cdd, *dd; /* * Find the booted controller. */ for (cdd = LIST_FIRST(ddlist); ctlr != 0 && cdd != LIST_END(ddlist); cdd = LIST_NEXT(cdd, dd_clist)) ctlr--; if (cdd == NULL) { /* * Oof, couldn't find it... */ return; } /* * Now find the device with the right slave/punit * that's a child of the controller. */ LIST_FOREACH(dd, &dev_data_list, dd_list) { /* * "sd" / "st" / "cd" -> "scsibus" -> "spc" * "hd" -> "hpibbus" -> "fhpib" */ if (dd->dd_dev->dv_parent->dv_parent != cdd->dd_dev) continue; if (dd->dd_slave == slave && dd->dd_punit == punit) { /* * Found it! */ bootdv = dd->dd_dev; break; } } } void setbootdev() { struct dev_data *cdd, *dd; int type, ctlr; /* * Note our magic numbers for type: * * 0 == ct * 2 == hd * 4 == scsi * 6 == le * * All are bdevsw major numbers, except for le, which * is just special. SCSI needs specific care since the * ROM wants to see 4, but depending upon the real device * we booted from, we might have a different major value. */ /* * Start with a clean slate. */ bootdev = 0; /* * If we don't have a saveable root_device, just punt. */ if (root_device == NULL) goto out; dd = dev_data_lookup(root_device); /* * If the root device is network, we're done * early. */ if (root_device->dv_class == DV_IFNET) { bootdev = MAKEBOOTDEV(6, 0, 0, 0, 0); goto out; } /* * Determine device type. */ if (device_match(root_device->dv_xname, "hd") == 0) type = 2; else if (device_match(root_device->dv_xname, "cd") == 0 || device_match(root_device->dv_xname, "sd") == 0 || device_match(root_device->dv_xname, "st") == 0) /* force scsi disk regardless of the actual device */ type = 4; else { printf("WARNING: strange root device!\n"); goto out; } /* * Get parent's info. * * "hd" -> "hpibbus" -> "fhpib" * "sd" / "cd" / "st" -> "scsibus" -> "spc" */ for (cdd = LIST_FIRST(&dev_data_list_hpib), ctlr = 0; cdd != LIST_END(&dev_data_list_hpib); cdd = LIST_NEXT(cdd, dd_clist), ctlr++) { if (cdd->dd_dev == root_device->dv_parent->dv_parent) { /* * Found it! */ bootdev = MAKEBOOTDEV(type, ctlr, dd->dd_slave, dd->dd_punit, DISKPART(rootdev)); break; } } out: /* Don't need this anymore. */ for (dd = LIST_FIRST(&dev_data_list); dd != LIST_END(&dev_data_list); ) { cdd = dd; dd = LIST_NEXT(dd, dd_list); free(cdd, M_DEVBUF); } } /* * Return the dev_data corresponding to the given device. */ static struct dev_data * dev_data_lookup(dev) struct device *dev; { struct dev_data *dd; LIST_FOREACH(dd, &dev_data_list, dd_list) if (dd->dd_dev == dev) return (dd); panic("dev_data_lookup"); } /* * Insert a dev_data into the provided list, sorted by select code. */ static void dev_data_insert(dd, ddlist) struct dev_data *dd; ddlist_t *ddlist; { struct dev_data *de; #ifdef DIAGNOSTIC if (dd->dd_scode < 0 || dd->dd_scode > 255) { panic("bogus select code for %s", dd->dd_dev->dv_xname); } #endif /* * Just insert at head if list is empty. */ if (LIST_EMPTY(ddlist)) { LIST_INSERT_HEAD(ddlist, dd, dd_clist); return; } /* * Traverse the list looking for a device who's select code * is greater than ours. When we find it, insert ourselves * into the list before it. */ for (de = LIST_FIRST(ddlist); LIST_NEXT(de, dd_clist) != LIST_END(ddlist); de = LIST_NEXT(de, dd_clist)) { if (de->dd_scode > dd->dd_scode) { LIST_INSERT_BEFORE(de, dd, dd_clist); return; } } /* * Our select code is greater than everyone else's. We go * onto the end. */ LIST_INSERT_AFTER(de, dd, dd_clist); } /********************************************************************** * Code to find and initialize the console **********************************************************************/ /* * Scan all select codes, passing the corresponding VA to (*func)(). * (*func)() is a driver-specific routine that looks for the console * hardware. */ void console_scan(func, arg) int (*func)(int, caddr_t, void *); void *arg; { int size, scode, sctop; caddr_t pa, va; /* * Scan all select codes. Check each location for some * hardware. If there's something there, call (*func)(). */ sctop = DIO_SCMAX(machineid); for (scode = 0; scode < sctop; ++scode) { /* * Skip over the select code hole and * the internal HP-IB controller. */ if (DIO_INHOLE(scode) || ((scode == 7) && internalhpib)) continue; /* Map current PA. */ pa = dio_scodetopa(scode); va = iomap(pa, PAGE_SIZE); if (va == NULL) continue; /* Check to see if hardware exists. */ if (badaddr(va)) { iounmap(va, PAGE_SIZE); continue; } /* * Hardware present, call callback. Driver returns * size of region to map if console probe successful * and worthwhile. */ size = (*func)(scode, va, arg); iounmap(va, PAGE_SIZE); if (size != 0 && conscode == scode) { /* Free last mapping. */ if (convasize) iounmap(conaddr, convasize); convasize = 0; /* Remap to correct size. */ va = iomap(pa, size); if (va == NULL) continue; /* Save this state for next time. */ conaddr = va; convasize = size; } } } int consolepass = -1; /* * Special version of cninit(). Actually, crippled somewhat. * This version lets the drivers assign cn_tab. */ void hp300_cninit(void) { struct consdev *cp; extern struct consdev constab[]; if (++consolepass == 0) { cn_tab = NULL; /* * Call all of the console probe functions. */ for (cp = constab; cp->cn_probe; cp++) (*cp->cn_probe)(cp); } /* * No console, we can handle it. */ if (cn_tab == NULL) return; /* * Turn on the console. * * Note that we need to check for cn_init because DIO frame buffers * will cause cn_tab to switch to wsdisplaycons, which does not * have an cn_init function. */ if (cn_tab->cn_init != NULL) { (*cn_tab->cn_init)(cn_tab); } } /********************************************************************** * Mapping functions **********************************************************************/ /* * Allocate/deallocate a cache-inhibited range of kernel virtual address * space mapping the indicated physical address range [pa - pa+size) */ caddr_t iomap(pa, size) caddr_t pa; int size; { vaddr_t iova, tva, off; paddr_t ppa; int error; if (size <= 0) return NULL; ppa = trunc_page((paddr_t)pa); off = (paddr_t)pa & PAGE_MASK; size = round_page(off + size); error = extent_alloc(extio, size, PAGE_SIZE, 0, EX_NOBOUNDARY, EX_NOWAIT | EX_MALLOCOK, &iova); if (error != 0) return (NULL); tva = iova; while (size != 0) { pmap_kenter_cache(tva, ppa, PG_RW | PG_CI); size -= PAGE_SIZE; tva += PAGE_SIZE; ppa += PAGE_SIZE; } pmap_update(pmap_kernel()); return ((void *)(iova + off)); } /* * Unmap a previously mapped device. */ void iounmap(va, size) caddr_t va; int size; { vaddr_t kva, off; int error; off = (vaddr_t)va & PAGE_MASK; kva = trunc_page((vaddr_t)va); size = round_page(off + size); pmap_kremove(kva, size); pmap_update(pmap_kernel()); error = extent_free(extio, kva, size, EX_NOWAIT); #ifdef DIAGNOSTIC if (error != 0) printf("iounmap: extent_free failed\n"); #endif } struct nam2blk nam2blk[] = { { "ct", 0 }, { "hd", 2 }, { "sd", 4 }, { "st", 7 }, { "rd", 8 }, { "cd", 9 }, { NULL, -1 } };