/* $OpenBSD: autoconf.c,v 1.8 2007/06/01 19:25:10 deraadt Exp $ */ /* OpenBSD: autoconf.c,v 1.64 2005/03/23 17:10:24 miod Exp */ /* * Copyright (c) 1996 * The President and Fellows of Harvard College. All rights reserved. * 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 Harvard University. * 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. * * @(#)autoconf.c 8.4 (Berkeley) 10/1/93 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #include #include #endif #include #include #include /* * The following several variables are related to * the configuration process, and are used in initializing * the machine. */ int fbnode; /* node ID of ROM's console frame buffer */ #ifdef KGDB extern int kgdb_debug_panic; #endif static int mbprint(void *, const char *); void sync_crash(void); int mainbus_match(struct device *, void *, void *); static void mainbus_attach(struct device *, struct device *, void *); struct bootpath bootpath[8]; int nbootpath; static void bootpath_build(void); char mainbus_model[30]; u_int prom_argc; char **prom_argv; char **prom_environ; vaddr_t prom_data; /* * locore.s code calls bootstrap() just before calling main(), after double * mapping the kernel to high memory and setting up the trap base register. * We must finish mapping the kernel properly and glean any bootstrap info. */ void bootstrap() { char **old_argv, **old_environ; u_int i, nenv; size_t asize; char *dst; bzero(&cpuinfo, sizeof(struct cpu_softc)); cpuinfo.master = 1; getcpuinfo(&cpuinfo, 0); /* * Compute how much memory is used by the PROM arguments. */ asize = prom_argc * sizeof(const char *); for (i = 0; i < prom_argc; i++) asize += 1 + strlen(prom_argv[i]); asize = roundup(asize, 4); for (nenv = 0; prom_environ[nenv] != NULL; nenv++) asize += 1 + strlen(prom_environ[i]); asize = roundup(asize, 4); asize += (1 + nenv) * sizeof(const char *); /* * Setup the intial mappings. */ pmap_bootstrap(asize); /* * Now that we have allocated memory for the commandline arguments * and the environment, save them. */ old_argv = prom_argv; prom_argv = (char **)prom_data; dst = (char *)(prom_data + prom_argc * sizeof(char *)); for (i = 0; i < prom_argc; i++) { prom_argv[i] = dst; asize = 1 + strlen(old_argv[i]); bcopy(old_argv[i], dst, asize); dst += asize; } old_environ = prom_environ; prom_environ = (char **)roundup((vaddr_t)dst, 4); dst = (char *)((vaddr_t)prom_environ + (1 + nenv) * sizeof(char *)); for (i = 0; i < nenv; i++) { prom_environ[i] = dst; asize = 1 + strlen(old_environ[i]); bcopy(old_environ[i], dst, asize); dst += asize; } prom_environ[nenv] = NULL; /* Moved zs_kgdb_init() to zs.c:consinit() */ #ifdef DDB db_machine_init(); ddb_init(); #endif } /* * bootpath_build: build a bootpath. Used when booting a generic * kernel to find our root device. Newer proms give us a bootpath, * for older proms we have to create one. An element in a bootpath * has 4 fields: name (device name), val[0], val[1], and val[2]. Note that: * Interpretation of val[] is device-dependent. Some examples: * * if (val[0] == -1) { * val[1] is a unit number (happens most often with old proms) * } else { * [sbus device] val[0] is a sbus slot, and val[1] is an sbus offset * [scsi disk] val[0] is target, val[1] is lun, val[2] is partition * [scsi tape] val[0] is target, val[1] is lun, val[2] is file # * } * */ static void bootpath_build() { u_int i; char *cp; /* XXX needs a rewrite for S4000 - we do not need to do things that way */ bzero(bootpath, sizeof(bootpath)); /* * The boot path is contained in argv[0] only. * It has the form: * [subdevice.]device([[ctrl],[unit],[partition]])[/]path */ printf("bootpath: %s\n", prom_argv[0]); /* * Remaining arguments are interpreted as options, with or without * leading dashes. */ for (i = 1; i < prom_argc; i++) { for (cp = prom_argv[i]; *cp != '\0'; cp++) switch (*cp) { case 'a': boothowto |= RB_ASKNAME; break; case 'b': boothowto |= RB_DFLTROOT; break; case 'c': boothowto |= RB_CONFIG; break; #ifdef DDB case 'd': Debugger(); break; #endif case 's': boothowto |= RB_SINGLE; break; } } } /* * save or read a bootpath pointer from the boothpath store. * * XXX. required because of SCSI... we don't have control over the "sd" * device, so we can't set boot device there. we patch in with * device_register(), and use this to recover the bootpath. */ struct bootpath * bootpath_store(storep, bp) int storep; struct bootpath *bp; { static struct bootpath *save; struct bootpath *retval; retval = save; if (storep) save = bp; return (retval); } /* * Determine mass storage and memory configuration for a machine. * We get the PROM's root device and make sure we understand it, then * attach it as `mainbus0'. We also set up to handle the PROM `sync' * command. */ void cpu_configure() { struct confargs oca; register int node = 0; register char *cp; int s; extern struct user *proc0paddr; /* build the bootpath */ bootpath_build(); if (boothowto & RB_CONFIG) { #ifdef BOOT_CONFIG user_config(); #else printf("kernel does not support -c; continuing..\n"); #endif } node = findroot(); oca.ca_ra.ra_node = node; oca.ca_ra.ra_name = cp = "mainbus"; if (config_rootfound(cp, (void *)&oca) == NULL) panic("mainbus not configured"); /* Enable device interrupts */ sta(GLU_ICR, ASI_PHYS_IO, ((lda(GLU_ICR, ASI_PHYS_IO) >> 24) & ~GICR_DISABLE_ALL) << 24); (void)spl0(); cold = 0; /* * Re-zero proc0's user area, to nullify the effect of the * stack running into it during auto-configuration. * XXX - should fix stack usage. */ s = splhigh(); bzero(proc0paddr, sizeof(struct user)); pmap_redzone(); splx(s); } void diskconf(void) { struct bootpath *bp; struct device *bootdv; /* * Configure swap area and related system * parameter based on device(s) used. */ bp = nbootpath == 0 ? NULL : &bootpath[nbootpath-1]; bootdv = (bp == NULL) ? NULL : bp->dev; setroot(bootdv, bp->val[2], RB_USERREQ | RB_HALT); dumpconf(); } /* * Console `sync' command. SunOS just does a `panic: zero' so I guess * no one really wants anything fancy... */ void sync_crash() { panic("PROM sync command"); } char * clockfreq(freq) register int freq; { register char *p; static char buf[10]; freq /= 1000; snprintf(buf, sizeof buf, "%d", freq / 1000); freq %= 1000; if (freq) { freq += 1000; /* now in 1000..1999 */ p = buf + strlen(buf); snprintf(p, buf + sizeof buf - p, "%d", freq); *p = '.'; /* now buf = %d.%3d */ } return (buf); } /* ARGSUSED */ static int mbprint(aux, name) void *aux; const char *name; { register struct confargs *ca = aux; if (name) printf("%s at %s", ca->ca_ra.ra_name, name); if (ca->ca_ra.ra_paddr) printf(" %saddr 0x%x", ca->ca_ra.ra_iospace ? "io" : "", (int)ca->ca_ra.ra_paddr); return (UNCONF); } /* * Given a `first child' node number, locate the node with the given name. * Return the node number, or 0 if not found. */ int findnode(first, name) int first; register const char *name; { register int node; for (node = first; node; node = nextsibling(node)) if (strcmp(getpropstring(node, "name"), name) == 0) return (node); return (0); } int mainbus_match(parent, self, aux) struct device *parent; void *self; void *aux; { struct cfdata *cf = self; register struct confargs *ca = aux; register struct romaux *ra = &ca->ca_ra; return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0); } /* * Attach the mainbus. * * Our main job is to attach the CPU (the root node we got in cpu_configure()) * and iterate down the list of `mainbus devices' (children of that node). * We also record the `node id' of the default frame buffer, if any. */ static void mainbus_attach(parent, dev, aux) struct device *parent, *dev; void *aux; { struct confargs oca; struct confargs *ca = aux; int node0; const char *model; node0 = ca->ca_ra.ra_node; /* i.e., the root node */ model = getpropstring(node0, "model"); if (model == NULL) model = sysmodel == SYS_S4000 ? "S4000" : "S4100"; strlcpy(mainbus_model, model, sizeof mainbus_model); printf(": %s\n", mainbus_model); /* * Locate and configure the ``early'' devices. These must be * configured before we can do the rest. For instance, the * EEPROM contains the Ethernet address for the LANCE chip. * If the device cannot be located or configured, panic. */ /* Configure the CPU. */ bzero(&oca, sizeof(oca)); oca.ca_bustype = BUS_MAIN; oca.ca_ra.ra_node = node0; oca.ca_ra.ra_name = "cpu"; (void)config_found(dev, (void *)&oca, mbprint); /* * XXX we don't support frame buffers, yet */ fbnode = 0; bzero(&oca, sizeof(oca)); oca.ca_bustype = BUS_MAIN; oca.ca_ra.ra_node = node0; oca.ca_ra.ra_name = "obio"; (void)config_found(dev, (void *)&oca, mbprint); } struct cfattach mainbus_ca = { sizeof(struct device), mainbus_match, mainbus_attach }; struct cfdriver mainbus_cd = { NULL, "mainbus", DV_DULL }; /* * findzs() is called from the zs driver (which is, at least in theory, * generic to any machine with a Zilog ZSCC chip). It should return the * address of the corresponding zs channel. It may not fail, and it * may be called before the VM code can be used. Here we count on the * FORTH PROM to map in the required zs chips. */ void * findzs(zs) int zs; { if (CPU_ISKAP) { struct rom_reg rr; register void *vaddr; switch (zs) { case 0: rr.rr_paddr = (void *)ZS0_BASE; break; case 1: rr.rr_paddr = (void *)ZS1_BASE; break; default: panic("findzs: unknown zs device %d", zs); } rr.rr_iospace = PMAP_OBIO; rr.rr_len = PAGE_SIZE; vaddr = mapiodev(&rr, 0, PAGE_SIZE); if (vaddr != NULL) return (vaddr); } panic("findzs: cannot find zs%d", zs); /* NOTREACHED */ } /* * Return a string property. There is a (small) limit on the length; * the string is fetched into a static buffer which is overwritten on * subsequent calls. */ char * getpropstring(node, name) int node; char *name; { register int len; static char stringbuf[32]; len = getprop(node, name, (void *)stringbuf, sizeof stringbuf - 1); if (len == -1) len = 0; stringbuf[len] = '\0'; /* usually unnecessary */ return (stringbuf); } /* * Fetch an integer (or pointer) property. * The return value is the property, or the default if there was none. */ int getpropint(node, name, deflt) int node; char *name; int deflt; { register int len; char intbuf[16]; len = getprop(node, name, (void *)intbuf, sizeof intbuf); if (len != 4) return (deflt); return (*(int *)intbuf); } int node_has_property(node, prop) /* returns 1 if node has given property */ register int node; register const char *prop; { return (getproplen(node, (char *)prop) != -1); } void callrom() { #ifdef notyet promvec->pv_abort(); #endif } /* * find a device matching "name" and unit number */ struct device * getdevunit(name, unit) char *name; int unit; { struct device *dev = TAILQ_FIRST(&alldevs); char num[10], fullname[16]; int lunit; /* compute length of name and decimal expansion of unit number */ snprintf(num, sizeof num, "%d", unit); lunit = strlen(num); if (strlen(name) + lunit >= sizeof(fullname) - 1) panic("config_attach: device name too long"); strlcpy(fullname, name, sizeof fullname); strlcat(fullname, num, sizeof fullname); while (strcmp(dev->dv_xname, fullname) != 0) { if ((dev = TAILQ_NEXT(dev, dv_list)) == NULL) return NULL; } return dev; } void device_register(struct device *dev, void *aux) { } struct nam2blk nam2blk[] = { { "sd", 7 }, { "st", 11 }, { "fd", 16 }, { "rd", 17 }, { "cd", 18 }, { NULL, -1 } };