/* $NetBSD: autoconf.c,v 1.41 2004/10/23 17:07:39 thorpej Exp $ */ /* * Bootpath canonicalization code * Copyright (C) 2004 Tim Kelly/Dialectronics * All rights reserved. * * Other code * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ #include __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.41 2004/10/23 17:07:39 thorpej Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void canonicalize_bootpath __P((void)); void ofw_stack __P((void)); extern char bootpath[256]; char cbootpath[256]; u_int *heathrow_FCR = NULL; /* * Determine device configuration for a machine. */ void cpu_configure() { int msr; init_interrupt(); calc_delayconst(); canonicalize_bootpath(); if (config_rootfound("mainbus", NULL) == NULL) panic("configure: mainbus not configured"); (void)spl0(); /* * Now allow hardware interrupts. */ asm volatile ("mfmsr %0; ori %0,%0,%1; mtmsr %0" : "=r"(msr) : "K"(PSL_EE|PSL_RI)); } void canonicalize_bootpath() { char *p, *lastp; int bnode, cnode; char buf[128]; int result = 0; /* * read to end of path * go back to first / * read forward to @ * reach ':' means no node, assume 0 * add node to canonized bootpath * correct flaws in OF if they occur */ printf("\nautoconf.c bootpath: %s\n", bootpath); strcpy(buf, bootpath); /* go to end of buf */ for (p = buf; *p != '\0' && p < (buf + sizeof(buf)); p++) ; /* read backwards, drop kernel name from buf */ for (; *p != '/' && p >= buf; p--) ; /* did we find /kernelname or just run out of buf? */ if (*p == '/') *p = '\0'; /* read back to locate disk node ID (sd@6) */ for (; p >= buf && *p != '@' && *p != '/'; p--) ; /* identify and save the disk node ID */ /* an @ indicates a disk node ID was specified */ if (*p == '@') bnode = *++p; else /* assign 0 */ bnode = '0'; /* if p == buf, OF_canon will kick out with -1 */ /* don't need buf, reuse it */ result = OF_canon(buf, buf, sizeof(buf)); if (result == -1) { /* Cannot canonicalize... use bootpath anyway. */ strcpy(cbootpath, bootpath); goto exit; } printf("autoconf.c OF_canon result: %s\n", buf); /* let's find out if the node ID was preserved */ /* repeat earlier steps */ for (p = buf; *p != '\0' && p < (buf + sizeof(buf)); p++) ; /* lastp stores the end of the buf string */ lastp = p; /* identify disk node ID returned */ /* back up to the node */ for (; p >= buf && *p != '/'; p--) ; /* find the disk ID */ for (; p <= lastp && *p != '@'; p++) ; if (p == lastp) { /* no node was preserved, append */ cnode = bnode; *lastp++ = '@'; *lastp++ = cnode; *lastp = '\0'; } else { /* we have an @ indicating an ID */ /* find out which, as some OF versions */ /* will return 0 from canon */ p++; cnode = *p; if (cnode != bnode) { /* wrong node returned, correct */ *p = bnode; *++p = '\0'; } else /* right node, terminate */ *++p = '\0'; } /* * we should have a consistency between bnode and cnode * at this point, so use buf and return */ strcpy(cbootpath, buf); exit: printf("autoconf.c cbootpath: %s\n", cbootpath); } #define DEVICE_IS(dev, name) \ (!strncmp(dev->dv_xname, name, sizeof(name) - 1) && \ dev->dv_xname[sizeof(name) - 1] >= '0' && \ dev->dv_xname[sizeof(name) - 1] <= '9') /* * device_register is called from config_attach as each device is * attached. We use it to find the NetBSD device corresponding to the * known OF boot device. */ void device_register(dev, aux) struct device *dev; void *aux; { static struct device *parent; static char *bp = bootpath + 1, *cp = cbootpath; unsigned long addr; char *p; if (booted_device) return; /* Skip over devices not represented in the OF tree. */ if (DEVICE_IS(dev, "mainbus")) { parent = dev; return; } if (DEVICE_IS(dev, "atapibus") || DEVICE_IS(dev, "pci") || DEVICE_IS(dev, "scsibus") || DEVICE_IS(dev, "atabus")) return; if (DEVICE_IS(dev->dv_parent, "atapibus") || DEVICE_IS(dev->dv_parent, "atabus") || DEVICE_IS(dev->dv_parent, "pci") || DEVICE_IS(dev->dv_parent, "scsibus")) { if (dev->dv_parent->dv_parent != parent) return; } else { if (dev->dv_parent != parent) return; } /* Get the address part of the current path component. The * last component of the canonical bootpath may have no * address (eg, "disk"), in which case we need to get the * address from the original bootpath instead. */ p = strchr(cp, '@'); if (!p) { if (bp) p = strchr(bp, '@'); if (!p) addr = 0; else { addr = strtoul(p + 1, NULL, 16); p = NULL; } } else addr = strtoul(p + 1, &p, 16); if (DEVICE_IS(dev->dv_parent, "mainbus")) { struct confargs *ca = aux; if (strcmp(ca->ca_name, "ofw") == 0) /* XXX */ return; if (addr != ca->ca_reg[0]) return; } else if (DEVICE_IS(dev->dv_parent, "pci")) { struct pci_attach_args *pa = aux; if (addr != pa->pa_device) return; } else if (DEVICE_IS(dev->dv_parent, "obio")) { struct confargs *ca = aux; if (addr != ca->ca_reg[0]) return; } else if (DEVICE_IS(dev->dv_parent, "scsibus") || DEVICE_IS(dev->dv_parent, "atapibus")) { struct scsipibus_attach_args *sa = aux; /* periph_target is target for scsi, drive # for atapi */ if (addr != sa->sa_periph->periph_target) return; } else if (DEVICE_IS(dev->dv_parent->dv_parent, "pciide")) { struct ata_device *adev = aux; if (addr != adev->adev_drv_data->drive) return; /* * OF splits channel and drive into separate path * components, so check the addr part of the next * component. (Ignore bp, because the canonical path * will be complete in the pciide case.) */ p = strchr(p, '@'); if (!p++) return; if (strtoul(p, &p, 16) != adev->adev_drv_data->drive) return; } else if (DEVICE_IS(dev->dv_parent->dv_parent, "wdc")) { struct ata_device *adev = aux; if (addr != adev->adev_drv_data->drive) return; } else return; /* If we reach this point, then dev is a match for the current * path component. */ printf(" (bootpath member) "); if (p && *p) { parent = dev; cp = p; bp = strchr(bp, '/'); if (bp) bp++; return; } else { booted_device = dev; booted_partition = 0; /* XXX -- should be extracted from bootpath */ return; } } /* * Setup root device. * Configure swap area. */ #include extern struct consdev *cn_tab; extern int ofkbd_cngetc(dev_t); void cpu_rootconf() { void *con_getc; printf("boot device: %s\n", booted_device ? booted_device->dv_xname : ""); con_getc = cn_tab->cn_getc; cn_tab->cn_getc = ofkbd_cngetc; setroot(booted_device, booted_partition); cn_tab->cn_getc = con_getc; } int #ifdef __STDC__ OF_interpret(char *cmd, int nreturns, ...) #else OF_interpret(cmd, nreturns, va_alist) char *cmd; int nreturns; va_dcl #endif { va_list ap; int i; static struct { char *name; int nargs; int nreturns; char *cmd; int status; int results[8]; } args = { "interpret", 1, 2, }; ofw_stack(); if (nreturns > 8) return -1; if ((i = strlen(cmd)) >= PAGE_SIZE) return -1; ofbcopy(cmd, OF_buf, i + 1); args.cmd = OF_buf; args.nargs = 1; args.nreturns = nreturns + 1; if (openfirmware(&args) == -1) return -1; va_start(ap, nreturns); for (i = 0; i < nreturns; i++) *va_arg(ap, int *) = args.results[i]; va_end(ap); return args.status; } /* * Find OF-device corresponding to the PCI device. */ int pcidev_to_ofdev(pc, tag) pci_chipset_tag_t pc; pcitag_t tag; { int bus, dev, func; u_int reg[5]; int p, q; int l, b, d, f; pci_decompose_tag(pc, tag, &bus, &dev, &func); for (q = OF_peer(0); q; q = p) { l = OF_getprop(q, "assigned-addresses", reg, sizeof(reg)); if (l > 4) { b = (reg[0] >> 16) & 0xff; d = (reg[0] >> 11) & 0x1f; f = (reg[0] >> 8) & 0x07; if (b == bus && d == dev && f == func) return q; } if ((p = OF_child(q))) continue; while (q) { if ((p = OF_peer(q))) break; q = OF_parent(q); } } return 0; } int getnodebyname(start, target) int start; const char *target; { int node, next; char name[64]; if (start == 0) start = OF_peer(0); for (node = start; node; node = next) { memset(name, 0, sizeof name); OF_getprop(node, "name", name, sizeof name - 1); if (strcmp(name, target) == 0) break; if ((next = OF_child(node)) != 0) continue; while (node) { if ((next = OF_peer(node)) != 0) break; node = OF_parent(node); } } return node; }