/* $NetBSD: autoconf.c,v 1.30 2012/10/13 06:38:08 tsutsui Exp $ */ /*- * Copyright (c) 1996 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Adam Glass, Gordon W. Ross, and Matthew Fredette. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time. Available devices are * determined (from possibilities mentioned in ioconf.c), and * the drivers are initialized. */ #include __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.30 2012/10/13 06:38:08 tsutsui Exp $"); #include "opt_kgdb.h" #include #include #include #include #include #include "locators.h" #include "scsibus.h" #if NSCSIBUS > 0 #include #include #include #endif /* NSCSIBUS > 0 */ #include #include #include #ifdef KGDB #include #endif /* * Do general device autoconfiguration, * then choose root device (etc.) * Called by sys/kern/subr_autoconf.c: configure() */ void cpu_configure(void) { /* * Consider stopping for a debugger before * autoconfiguration. */ if (boothowto & RB_KDB) { #ifdef KGDB /* XXX - Ask on console for kgdb_dev? */ /* Note: this will just return if kgdb_dev==NODEV */ kgdb_connect(1); #else /* KGDB */ /* Either DDB or no debugger (just PROM). */ Debugger(); #endif /* KGDB */ } /* General device autoconfiguration. */ if (config_rootfound("mainbus", NULL) == NULL) panic("%s: mainbus not found", __func__); /* * Now that device autoconfiguration is finished, * we can safely enable interrupts. */ printf("enabling interrupts\n"); (void)spl0(); } static int mainbus_match(device_t, cfdata_t, void *); static void mainbus_attach(device_t, device_t, void *); CFATTACH_DECL_NEW(mainbus, 0, mainbus_match, mainbus_attach, NULL, NULL); /* * Probe for the mainbus; always succeeds. */ static int mainbus_match(device_t parent, cfdata_t cf, void *aux) { return 1; } /* * Do "direct" configuration for the bus types on mainbus. * This controls the order of autoconfig for important things * used early. For example, idprom is used by Ether drivers. */ static void mainbus_attach(device_t parent, device_t self, void *args) { struct mainbus_attach_args ma; const char *const *cpp; static const char *const special[] = { /* find these first */ "obio", "obmem", NULL }; aprint_normal("\n"); ma.ma_bustag = &mainbus_space_tag; ma.ma_dmatag = &mainbus_dma_tag; ma.ma_paddr = LOCATOR_FORBIDDEN; ma.ma_pri = LOCATOR_FORBIDDEN; /* Find all `early' mainbus buses */ for (cpp = special; *cpp != NULL; cpp++) { ma.ma_name = *cpp; (void)config_found(self, &ma, NULL); } /* Find the remaining buses */ ma.ma_name = NULL; (void)config_found(self, &ma, NULL); /* Lastly, find the PROM console */ ma.ma_name = "pcons"; (void)config_found(self, &ma, NULL); } /* * sun68k_bus_search: * This function is passed to config_search_ia() by the attach function * for each of the "bus" drivers (obio, obmem, mbmem, vme, ...). * The purpose of this function is to copy the "locators" into our * _attach_args structure, so child drivers may use the _attach_args both * as match parameters and as temporary storage for the defaulted * locator values determined in the child_match and preserved for * the child_attach function. If the bus attach functions just * used config_found, then we would not have an opportunity to * setup the _attach_args for each child match and attach call. */ int sun68k_bus_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) { struct mainbus_attach_args *map = aux; struct mainbus_attach_args ma; /* Check whether we're looking for a specifically named device */ if (map->ma_name != NULL && strcmp(map->ma_name, cf->cf_name) != 0) return 0; #ifdef DIAGNOSTIC if (cf->cf_fstate == FSTATE_STAR) panic("%s: FSTATE_STAR", __func__); #endif /* * Prepare to copy the locators into our _attach_args. */ ma = *map; ma.ma_name = NULL; /* * Avoid entries which are missing attach information that * they need, or that have attach information that they * cannot have. The individual bus attach functions tell * us this by initializing the locator fields in the attach * args they provide us. * * At the same time we copy these values into the _attach_args * will pass to the device's match and attach functions. */ #ifdef DIAGNOSTIC #define BAD_LOCATOR(ma_loc, what) \ panic("%s: %s %s for: %s%d", __func__, \ map->ma_loc == LOCATOR_REQUIRED ? "missing" : "unexpected", \ what, cf->cf_name, cf->cf_unit) #else #define BAD_LOCATOR(ma_loc, what) return 0 #endif #define CHECK_LOCATOR(ma_loc, cf_loc, what) \ if ((map->ma_loc == LOCATOR_FORBIDDEN && cf->cf_loc != -1) || \ (map->ma_loc == LOCATOR_REQUIRED && cf->cf_loc == -1)) \ BAD_LOCATOR(ma_loc, what); \ else \ ma.ma_loc = cf->cf_loc CHECK_LOCATOR(ma_paddr, cf_loc[MBIOCF_ADDR], "address"); CHECK_LOCATOR(ma_pri, cf_loc[MBIOCF_IPL], "ipl"); /* * Note that this allows the match function to save * defaulted locators in the _attach_args that will be * preserved for the related attach call. * XXX - This is a hack... */ if (config_match(parent, cf, &ma) > 0) { config_attach(parent, cf, &ma, sun68k_bus_print); } return 0; } /* * sun68k_bus_print: * Just print out the final (non-default) locators. * The parent name is non-NULL when there was no match * found by config_found(). */ int sun68k_bus_print(void *args, const char *name) { struct mainbus_attach_args *ma = args; if (name) aprint_normal("%s:", name); if (ma->ma_paddr != -1) aprint_normal(" addr 0x%x", (unsigned int) ma->ma_paddr); if (ma->ma_pri != -1) aprint_normal(" ipl %d", ma->ma_pri); return UNCONF; } /****************************************************************/ /* This takes the args: name, ctlr, unit */ typedef device_t (*findfunc_t)(char *, int, int); static device_t net_find(char *, int, int); #if NSCSIBUS > 0 static device_t scsi_find(char *, int, int); #endif /* NSCSIBUS > 0 */ static device_t xx_find(char *, int, int); struct prom_n2f { const char name[4]; findfunc_t func; }; static struct prom_n2f prom_dev_table[] = { { "ie", net_find }, { "ec", net_find }, { "le", net_find }, #if NSCSIBUS > 0 { "sd", scsi_find }, #endif /* NSCSIBUS > 0 */ { "xy", xx_find }, { "xd", xx_find }, { "", 0 }, }; /* * This converts one hex number to an integer, and returns * an updated string pointer. */ static const char *str2hex(const char *, int *); static const char * str2hex(const char *p, int *_val) { int val; int c; for (val = 0;; val = (val << 4) + c, p++) { c = *((const unsigned char *)p); if (c >= 'a') c-= ('a' + 10); else if (c >= 'A') c -= ('A' + 10); else if (c >= '0') c -= '0'; if (c < 0 || c > 15) break; } *_val = val; return p; } /* * Choose root and swap devices. */ void cpu_rootconf(void) { struct prom_n2f *nf; const char *devname; findfunc_t find; char promname[4]; char partname[4]; const char *prompath; int prom_ctlr, prom_unit, prom_part; /* Get the PROM boot path and take it apart. */ prompath = prom_getbootpath(); if (prompath == NULL) prompath = "zz(0,0,0)"; promname[0] = *(prompath++); promname[1] = *(prompath++); promname[2] = '\0'; prom_ctlr = prom_unit = prom_part = 0; if (*prompath == '(' && *(prompath = str2hex(++prompath, &prom_ctlr)) == ',' && *(prompath = str2hex(++prompath, &prom_unit)) == ',') (void)str2hex(++prompath, &prom_part); /* Default to "unknown" */ booted_device = NULL; booted_partition = 0; devname = ""; partname[0] = '\0'; find = NULL; /* Do we know anything about the PROM boot device? */ for (nf = prom_dev_table; nf->func; nf++) if (!strcmp(nf->name, promname)) { find = nf->func; break; } if (find) booted_device = (*find)(promname, prom_ctlr, prom_unit); if (booted_device) { devname = device_xname(booted_device); if (device_class(booted_device) == DV_DISK) { booted_partition = prom_part & 7; partname[0] = 'a' + booted_partition; partname[1] = '\0'; } } printf("boot device: %s%s\n", devname, partname); rootconf(); } /* * Functions to find devices using PROM boot parameters. */ /* * Network device: Just use controller number. */ static device_t net_find(char *name, int ctlr, int unit) { return device_find_by_driver_unit(name, ctlr); } #if NSCSIBUS > 0 /* * SCSI device: The controller number corresponds to the * scsibus number, and the unit number is (targ*8 + LUN). */ static device_t scsi_find(char *name, int ctlr, int unit) { device_t scsibus; struct scsibus_softc *sbsc; struct scsipi_periph *periph; int target, lun; if ((scsibus = device_find_by_driver_unit("scsibus", ctlr)) == NULL) return NULL; /* Compute SCSI target/LUN from PROM unit. */ target = prom_sd_target((unit >> 3) & 7); lun = unit & 7; /* Find the device at this target/LUN */ sbsc = device_private(scsibus); periph = scsipi_lookup_periph(sbsc->sc_channel, target, lun); if (periph == NULL) return NULL; return periph->periph_dev; } #endif /* NSCSIBUS > 0 */ /* * Xylogics SMD disk: (xy, xd) * Assume wired-in unit numbers for now... */ static device_t xx_find(char *name, int ctlr, int unit) { return device_find_by_driver_unit(name, ctlr * 2 + unit); }