#ident "@(#)autoconf.c 1.1 94/10/31 SMI" /* * Copyright (c) 1987-1991 by Sun Microsystems, Inc. */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time and initializes the Mainbus * device tables and the memory controller monitoring. Available * devices are determined by PROM and passed into configure(), where * 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 #undef NFS #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 #if defined(SUN4M_35) extern int no_vme; #endif extern int swap_present; /* * The following several variables are related to * the configuration process, and are used in initializing * the machine. */ int dkn; /* number of iostat dk numbers assigned so far */ /* * array of "name"/unit pairs that map to dk numbers */ struct dk_ivec dk_ivec[DK_NDRIVE]; /* * pointer to DVMA resource map */ #ifdef IOMMU struct map *vme32map; struct map *vme24map; struct map *altsbusmap; struct map *sbusmap; struct map *bigsbusmap; struct map *mbutlmap; /* this is here to keep non-OBP driver happay */ struct mb_hd mb_hd; #endif struct map *dvmamap; struct dev_opslist *dev_opslist = 0; extern struct dev_opslist av_opstab[]; struct dev_info *top_devinfo; struct dev_info *options_devinfo; #ifdef VME struct dev_info *vme_devinfo; /* To store vme dev_info pointer */ int found_vme = 0; struct vme_spaces { char *name; /* */ unsigned cookie; unsigned space; unsigned base; } vme_spaces[] = { { "vme16d16", SP_VME16D16, 0xC, 0xFFFF0000 }, { "vme24d16", SP_VME24D16, 0xC, 0xFF000000 }, { "vme32d16", SP_VME32D16, 0xC, 0x00000000 }, { "vme16d32", SP_VME16D32, 0xD, 0xFFFF0000 }, { "vme24d32", SP_VME24D32, 0xD, 0xFF000000 }, { "vme32d32", SP_VME32D32, 0xD, 0x00000000 }, { NULL }, }; void add_ipi_drive(); #endif VME int svimap[] = { 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0 }; #define LPL2SPL(n) ((n)&15) #define LPL2SVL(n) svimap[LPL2SPL(n)] static char *out_of_range = "Warning: Out of range %s specification in device node <%s>\n"; /* * Static function to determine if a reg prop is enclosed within * a given a range spec. */ static int reg_is_enclosed_in_range(rp, rangep) struct dev_reg *rp; struct dev_rng *rangep; { if (rp->reg_bustype != rangep->rng_cbustype) return (0); if ((int)rp->reg_addr < rangep->rng_coffset) return (0); if (rangep->rng_size == 0) return (1); /* size is really 2**(bits_per_word) */ if (((int)rp->reg_addr + rp->reg_size - 1) <= (rangep->rng_offset + rangep->rng_size - 1)) return (1); return (0); } void apply_range_to_reg(name, nrange, rangep, nreg, regp) char *name; int nrange; struct dev_rng *rangep; int nreg; struct dev_reg *regp; { register int i; register struct dev_rng *rp; if (nrange>0) while (nreg-->0) { for (i = 0, rp = rangep; i < nrange; ++i, ++rp) { if (reg_is_enclosed_in_range(regp, rp)) break; } if (i == nrange) { printf(out_of_range, "register", name); continue; } regp->reg_addr += (u_int)rp->rng_offset; regp->reg_bustype = (int)rp->rng_bustype; ++regp; } } void apply_range_to_range(name, nr, pp, nc, cp) char *name; int nr; /* Number of Parent ranges */ struct dev_rng *pp; /* Parent ranges */ int nc; /* Number of child ranges */ struct dev_rng *cp; /* Child ranges */ { register int i; register struct dev_rng *rp; if (nr>0) while (nc-->0) { for (i = 0, rp = pp; i < nr; ++i, ++rp) if (cp->rng_bustype == rp->rng_cbustype) break; if (i == nr) { printf(out_of_range, "range", name); continue; } cp->rng_offset += rp->rng_offset; cp->rng_bustype = rp->rng_bustype; ++cp; } } /* * This routine fills in the common information for a given dev_info struct. * Using the nodeid that is already initialized, it pulls the following * information out of the PROMS: * name, reg, range, intr * If any of these properties are not found, the value of that field is * left at its initial value. * * nreg && nintr are inferred from the property length of reg && intr resp. */ void fill_devinfo(dev_info) struct dev_info *dev_info; { int nodeid; static int curzs = 0; nodeid = dev_info->devi_nodeid; dev_info->devi_name = (char *)getlongprop(nodeid, "name"); #ifdef SUN4M_690 /* * XXX: Since we're using the generic attach_devs function for * XXX: children of the "SUNW,pn" node, and it does not follow the * XXX: "obio" model, we need to escape at this point... * XXX: (The reg property in the "ipi3sc" node does not describe * XXX: byte addressable space in it's parent's space.) */ if (strcmp(dev_info->devi_name, "ipi3sc") == 0) return; #endif SUN4M_690 /* * We try to match stdin/stdin (which is the "second" zs, * if console/kbd) while attaching the first zs device. * We need the unit number initialized. */ if (strcmp("zs", dev_info->devi_name) == 0) dev_info->devi_unit = curzs++; dev_info->devi_nintr = getproplen(nodeid, "intr") / sizeof (struct dev_intr); if (dev_info->devi_nintr > 0) dev_info->devi_intr = (struct dev_intr *)getlongprop(nodeid, "intr"); dev_info->devi_nreg = getproplen(nodeid, "reg") / sizeof (struct dev_reg); if (dev_info->devi_nreg > 0) { dev_info->devi_reg = (struct dev_reg *)getlongprop(nodeid, "reg"); /* * The "reg" property is relative to the * range specified by the node's parent. * Convert the relative data from the prom into * absolute data by combining with the absolute * range information stored in the parent. */ if ((dev_info->devi_parent) && (dev_info->devi_parent->devi_nrng > 0) && (dev_info->devi_parent->devi_rng)) apply_range_to_reg(dev_info->devi_name, dev_info->devi_parent->devi_nrng, dev_info->devi_parent->devi_rng, dev_info->devi_nreg, dev_info->devi_reg); } dev_info->devi_nrng = getproplen(nodeid, "ranges") / sizeof (struct dev_rng); if (dev_info->devi_nrng > 0) { dev_info->devi_rng = (struct dev_rng *)getlongprop(nodeid, "ranges"); /* * The "ranges" property is relative to the * range specified by the node's parent. * Convert the relative data from the prom into * absolute data by combining with the absolute * range information stored in the parent. */ if ((dev_info->devi_parent) && (dev_info->devi_parent->devi_nrng > 0) && (dev_info->devi_parent->devi_rng)) apply_range_to_range(dev_info->devi_name, dev_info->devi_parent->devi_nrng, dev_info->devi_parent->devi_rng, dev_info->devi_nrng, dev_info->devi_rng); } else if (dev_info->devi_parent) { /* inherit range from parent */ dev_info->devi_nrng = dev_info->devi_parent->devi_nrng; dev_info->devi_rng = dev_info->devi_parent->devi_rng; } else { /* root node. default is "nothing". */ dev_info->devi_nrng = 0; dev_info->devi_rng = (struct dev_rng *)0; } } /* * This routine attempts to match a device name to a device driver. It * walks the list of driver ops_vectors, calling the identify routines * until one of the drivers claims the device. */ struct dev_ops * match_driver(name) char *name; { struct dev_opslist *ops; for (ops = dev_opslist; ops->devl_ops != NULL; ops = ops->devl_next) if (ops->devl_ops->devo_identify(name)) return (ops->devl_ops); return (NULL); } /* * This routine checks to see if the given nodeid is already listed as a * child of the given device. It is used to prevent duplicate dev_info * structs from being created. */ static int check_dupdev(dev_info, nodeid) struct dev_info *dev_info; int nodeid; { struct dev_info *dev; for (dev = dev_info->devi_slaves; dev != NULL; dev = dev->devi_next) { if (dev->devi_nodeid == nodeid) return (1); } return (0); } #ifdef VME /* Check if device already a child of vme */ /*ARGSUSED*/ static int vme_check_dupdev(dev_info, name, unit) struct dev_info *dev_info; char *name; int unit; { struct dev_info *dev; #ifdef DEBUG_IPI printf("vme_check_dupdev: looking for dup of %s, unit#0x%X\n", name, unit); #endif /* * %%% HACK for building the ipi related dev_info * nodes. We are throwing out everything we dont need. * The IPI related devinfo nodes are created via the pseudo * (sbus style) drivers included in this file. */ if ((strcmp(name, "isc") == 0) || (strcmp(name, "is") == 0) || (strcmp(name, "idc") == 0) || (strcmp(name, "id") == 0)) { #ifdef DEBUG_IPI printf("vme_check_dupdev: THROWING OUT %s, unit#0x%X\n", name, unit); #endif return (1); } for (dev = dev_info->devi_slaves; dev != NULL; dev = dev->devi_next) { if (strcmp(dev->devi_name, name) == 0) { if (dev->devi_unit == unit) { #ifdef DEBUG_IPI printf("vme_check_dupdev: FOUND dup of %s, unit#0x%X\n", name, unit); #endif return (1); /* found a dup */ } } } #ifdef DEBUG_IPI printf("vme_check_dupdev: found NO dup of %s, unit#0x%X\n", name, unit); #endif return (0); } #endif VME /* * This routine appends the second argument onto the slave list for the * first argument. It always appends it to the end of the list. */ void append_dev(dev_parent, dev_slave) struct dev_info *dev_parent, *dev_slave; { register struct dev_info *dev; if (dev_parent->devi_slaves == NULL) dev_parent->devi_slaves = dev_slave; else { for (dev = dev_parent->devi_slaves; dev->devi_next != NULL; dev = dev->devi_next) ; dev->devi_next = dev_slave; } } #ifndef VDDRV /* * This routine removes the second argument from the slave list for the * first argument. It returns -1 if the slave isn't found on the list. */ static int remove_dev(dev_parent, dev_slave) struct dev_info *dev_parent, *dev_slave; { register struct dev_info *dev; if (dev_parent->devi_slaves == dev_slave) { dev_parent->devi_slaves = dev_slave->devi_next; return (0); } else for (dev = dev_parent->devi_slaves; dev != NULL; dev = dev->devi_next) { if (dev->devi_next == dev_slave) { dev->devi_next = dev_slave->devi_next; return (0); } } return (-1); } #endif VDDRV /* * Create a list of ops vectors out of the array config produced. */ static void init_opslist() { register struct dev_opslist *dp; register int i = 0; while ((dp = &av_opstab[i])->devl_ops != NULL) dp->devl_next = &av_opstab[++i]; dev_opslist = av_opstab; } #define DEFAULT_SPLVM (ipltospl(9)) /* * Maximum interrupt priority used by Mainbus DMA. * This value is determined as each driver that uses DMA calls adddma * with the hardware interrupt priority level that they use. Adddma * increases splvm_val as necessary, and computes SPLMB based on it. * * XXX SPLMB is for backward compatibility only. * XXX Drivers should call splvm() instead of splr(pritospl(SPLMB)). */ int SPLMB = spltopri(DEFAULT_SPLVM); /* reasonable default */ /* * We use splvmval to implement splvm(). */ int splvm_val = DEFAULT_SPLVM; /* reasonable default */ extern struct machinfo mach_info; /* * Machine type we are running on. */ int cpu; /* * Determine mass storage and memory configuration for a machine. * Get cpu type, and then switch out to machine specific procedures * which will determine what is out there. */ configure() { extern int fpu_exists; /* set or cleared by fpu_probe */ idprom(); fpu_probe(); if (!fpu_exists) printf("No FPU in configuration\n"); /* * Configure devinfo-style devices. */ sbus_config(); /* * Configure VME (mainbus-style) devices. */ mbconfig(); /* * Attach pseudo-devices */ pseudoconfig(); } /* * Initialize devices on the machine. * Uses configuration tree built by the PROMs to determine what * is present. The attach_devs() routine is used in conjuction with * the sbus routines to cause the appropriate initialization * to occur. All we do here is start the ball rolling. */ #ifndef NOPROM sbus_config() { extern dnode_t prom_nextnode(); /* * chain together the opslist that we're built with */ init_opslist(); /* * build the dev_info tree, attaching devices along the way */ top_devinfo = (struct dev_info *) kmem_zalloc(sizeof (*top_devinfo)); top_devinfo->devi_nodeid = (int)prom_nextnode((dnode_t)0); fill_devinfo(top_devinfo); attach_devs(top_devinfo); } #else NOPROM /* * SAS doesn't not simulate the open prom, and only has a * simulated console and simulated disk. The attach and probe * routines of these simulated devices don't do much, so there * is probably not much to do here. May have to add more here * though, so that data structures get initialized? * console: * simcprobe: does nothing. * simcattach: does a little bit of initialization. * simdprobe: doesn't exist. * simdattach: does nothing. */ sbus_config() { #ifdef SAS simcattach(); /* probably will have to do more for SAS */ #endif } #endif NOPROM int obio_identify(name) char *name; { if (strcmp(name, "obio")) return 0; return 1; } int obio_attach(devi) struct dev_info *devi; { attach_devs(devi); return (0); } struct dev_ops obio_ops = { 1, obio_identify, obio_attach, }; int iommu_identify(name) char *name; { if (strcmp(name, "iommu")) return 0; return 1; } int iommu_attach(devi) struct dev_info *devi; { attach_devs(devi); return (0); } struct dev_ops iommu_ops = { 1, iommu_identify, iommu_attach, }; int sbus_identify(name) char *name; { if (strcmp(name, "sbus")) return 0; return 1; } int sbus_attach(devi) struct dev_info *devi; { attach_devs(devi); return (0); } struct dev_ops sbus_ops = { 1, sbus_identify, sbus_attach, }; #ifdef VME int vme_identify(name) char *name; { if (strcmp(name, "vme")) return (0); return (1); } int vme_attach(devi) struct dev_info *devi; { attach_devs(devi); return (0); } struct dev_ops vme_ops = { 1, vme_identify, vme_attach, }; #endif VME #ifdef SUN4M_690 /* * Pseudo device drivers for Self-identifying VME/IPI */ int pn_identify(name) char *name; { if (strcmp(name, "SUNW,pn") == 0) return (1); return (0); } int pn_attach(devi) register struct dev_info *devi; { /* * XXX: Stop report_dev from printing all 5 sets of regs and intrs. */ register int nregs = devi->devi_nreg; register int nintr = devi->devi_nintr; devi->devi_nreg = (nregs > 0 ? 1 : 0); devi->devi_nintr = (nintr > 0 ? 1 : 0); report_dev(devi); devi->devi_nreg = nregs; devi->devi_nintr = nintr; attach_devs(devi); return (0); } struct dev_ops pn_ops = { 1, pn_identify, pn_attach, }; int idpseudo_identify(name) char *name; { if (strcmp(name, "id") == 0) return (1); return (0); } /*ARGSUSED*/ int idpseudo_attach(devi) register struct dev_info *devi; { #ifdef IPI_DEBUG register struct dev_info *pdp = devi->devi_parent; printf("%s%d at %s%d", devi->devi_name, devi->devi_unit, pdp->devi_name, pdp->devi_unit); #endif IPI_DEBUG return (0); } struct dev_ops idpseudo_ops = { 1, idpseudo_identify, idpseudo_attach, }; int ipi3sc_identify(name) char *name; { #ifdef IPI_DEBUG prom_printf("ipi3sc_identify: name %s\n", name); #endif IPI_DEBUG if (strcmp(name, "ipi3sc") == 0) return (1); #ifndef NO_IDC_COMPATIBILITY /* * XXX: For compatability with pre-FCS PROMS, also identify "idc". */ if (strcmp(name, "idc") == 0) return (1); #endif !NO_IDC_COMPATIBILITY return (0); } /* * XXX: Fill in the unit number, properly, and create "id" children. * XXX: This is a terrible hack, that augments the mbconfig stuff, * XXX: basically, so we can ignore it, later. Assumption is 8 drives * XXX: per slave controller. */ int ipi3sc_attach(devi) struct dev_info *devi; { register struct dev_reg *rp; register struct dev_info *dp; int n, length; #ifdef IPI_DEBUG prom_printf("ipi3sc_attach...\n"); #endif IPI_DEBUG /* * For device "ipi3sc", the channel number is expressed in the * "bustype" field of the device's reg property (which does not * refer to byte addressable space). */ length = getproplen(devi->devi_nodeid, "reg"); if (length == -1) length = 0; ASSERT(length != 0); rp = (struct dev_reg *)getlongprop(devi->devi_nodeid, "reg"); n = devi->devi_unit = rp->reg_bustype; #ifndef NO_IDC_COMPATIBILITY if (strcmp("ipi3sc", devi->devi_name) != 0) { /* For idc */ /* * For device "idc", * the channel number is computed from the * address field. */ n = devi->devi_unit = (int)(((int)rp->reg_addr & 0x1c00) >> 10); } #endif NO_IDC_COMPATIBILITY (void)kmem_free((caddr_t)rp, (u_int)length); dp = devi->devi_parent; #ifndef NO_IDC_COMPATIBILITY if (strcmp("ipi3sc", devi->devi_name) == 0) #endif NO_IDC_COMPATIBILITY printf("%s channel #%d at %s%d\n", devi->devi_name, n, dp->devi_name, dp->devi_unit); #ifndef NO_IDC_COMPATIBILITY else report_dev(devi); #endif NO_IDC_COMPATIBILITY return (0); } struct dev_ops ipisc_ops = { 1, ipi3sc_identify, ipi3sc_attach, }; #endif SUN4M_690 /* For VME/Old style drivers */ static struct dev_ops pseudo_ops = { 0 }; /* * This routine identifies and initializes all the slaves of the given * device. A dev_info struct is created for each slave, and the common * values initialized. It is tested against the device driver * identify routines, and a match causes the appropriate dev_ops pointer * to be linked to the device. After all the slaves have been identified, * the appropriate attach routines are called for each one. Doing it in this * order allows a driver to count how many devices it will have before the * first attach routine is called. If an attach fails for any reason, the * dev_info struct is marked unattached, by setting the dev_ops pointer * to NULL. * If no identify match is made for a device, it is also marked * unattached and an error message is printed. * This routine should not be called after the system is running to initialize * a device that was added on the fly; add_drv should be used for that * purpose. * * It carefully checks each device the * prom returns to avoid creating duplicate dev_info entries, * but we may not need to do that anymore since we don't use this to add * devices after boot. */ attach_devs(dev_info) struct dev_info *dev_info; { register struct dev_info *dev; struct dev_info *newdevs = NULL; register int curnode; #ifdef TRACK_RANGES_IN_DEV_INFO struct dev_reg *nrp; int nrng, mrng, i; int npr = 0; struct dev_reg *prp = 0; /* parent's range info */ struct dev_reg *rp = 0; #endif extern dnode_t prom_nextnode(), prom_childnode(); curnode = (int)prom_childnode((dnode_t)dev_info->devi_nodeid); if (curnode == 0) return; for (; curnode != 0; curnode = (int)prom_nextnode((dnode_t)curnode)) { /* * If it's already on the list, skip it. */ if (check_dupdev(dev_info, curnode) != 0) continue; /* * Allocate it, fill it in and add it to the list. */ dev = (struct dev_info *)kmem_zalloc(sizeof (*dev)); append_dev(dev_info, dev); dev->devi_nodeid = curnode; dev->devi_parent = dev_info; fill_devinfo(dev); dev->devi_driver = match_driver(dev->devi_name); #ifdef VME /* Save away the dev_info pointer for vme */ if (!found_vme) { if (strcmp(dev->devi_name, "vme") == 0) { vme_devinfo = dev; found_vme = 1; } } #endif VME /* * Note the first new struct on the list. */ if (newdevs == NULL) newdevs = dev; } /* * Try to attach the new devs. If any of the identifies or attaches * fail, throw out the dev_info struct. * We track this loop funny because structs can get deallocated in * the middle. */ for (dev = newdevs; dev != NULL; dev = newdevs) { newdevs = dev->devi_next; if (dev->devi_driver == NULL) { #if !defined(VDDRV) && !defined(MUNIX) && !defined(MINIROOT) if (remove_dev(dev_info, dev)) panic("attach_devs remove_dev 1"); (void) kmem_free((caddr_t) dev, sizeof (*dev)); #endif VDDRV && MUNIX && MINIROOT continue; } if ((*dev->devi_driver->devo_attach)(dev) < 0) { #if defined(VDDRV) || defined(MUNIX) || defined(MINIROOT) dev->devi_driver = NULL; #else VDDRV || MUNIX || MINIROOT if (remove_dev(dev_info, dev)) panic("attach_devs remove_dev 2"); (void) kmem_free((caddr_t) dev, sizeof (*dev)); #endif VDDRV || MUNIX || MINIROOT } } } #ifdef VDDRV struct add_drv_info { struct dev_ops *adi_ops; int adi_count; }; struct new_devlist { struct new_devlist *nd_next; struct dev_info *nd_dev; int nd_attachme; }; struct new_devlist_head { struct new_devlist *ndh_head; struct new_devlist *ndh_tail; }; static struct new_devlist_head new_devlist_head; /* * Pick up devices for a just-loaded driver: * 1. Initialize the add_drv_info structure. * 2. Kick off add_drv_layer on the top layer of devices. * 3. Return the number of devices attached. */ int add_drv(dev_ops) struct dev_ops *dev_ops; { struct add_drv_info adi; register struct add_drv_info *adip = &adi; void add_drv_layer(); register struct new_devlist *ndp; int attached = 0; adi.adi_ops = dev_ops; adi.adi_count = 0; new_devlist_head.ndh_head = new_devlist_head.ndh_tail = NULL; /* * build up a list of drivers */ walk_slaves(top_devinfo, (int (*)()) add_drv_layer, (caddr_t) &adi); /* * identify the drivers in the list */ for (ndp = new_devlist_head.ndh_head; ndp; ndp = ndp->nd_next) ndp->nd_attachme = (*adip->adi_ops->devo_identify) (ndp->nd_dev->devi_name); /* * attach the drivers int the list and cleanup */ for (ndp = new_devlist_head.ndh_head; ndp; ndp = new_devlist_head.ndh_head) { if (ndp->nd_attachme) { ndp->nd_dev->devi_driver = adip->adi_ops; if ((*adip->adi_ops->devo_attach) (ndp->nd_dev) < 0) ndp->nd_dev->devi_driver = NULL; else attached++; } new_devlist_head.ndh_head = ndp->nd_next; (void) kmem_free((caddr_t) ndp, sizeof (*ndp)); } adip->adi_count += attached; return (adi.adi_count); } /* * Pick up devices in a given layer for a just-loaded driver: * 1. Walk the layer, finding any unattached devices. * 2. Call the identify routine for each unattached device, and mark * those that we should attempt to attach. * 3. Attach those that have been so marked. * 4. Free the link of new_devlist structures. * 5. Update the count of attached devices. * 6. Recurse over any slaves of this layer. */ static void add_drv_layer(dev, adip) struct dev_info *dev; struct add_drv_info *adip; { void add_a_device(); walk_layer(dev, (int (*)()) add_a_device, (caddr_t) &new_devlist_head); /* Now see if any slaves of this layer need identifying */ walk_slaves(dev, (int (*)()) add_drv_layer, (caddr_t) adip); } /* * If a dev_info is unattached, add it to the new_devlist being built. * Keep the list in FIFO order. */ static void add_a_device(dev, ndhp) struct dev_info *dev; struct new_devlist_head *ndhp; { struct new_devlist *ndp; if (dev->devi_driver == NULL) { ndp = (struct new_devlist *) kmem_alloc(sizeof (*ndp)); ndp->nd_dev = dev; if (ndhp->ndh_head == NULL) ndhp->ndh_head = ndp; else /* if head is set, so is tail */ ndhp->ndh_tail->nd_next = ndp; ndp->nd_next = NULL; ndhp->ndh_tail = ndp; } } /* * Add a driver to the list of ops vectors. Don't allow duplicates. */ int add_ops(ops) struct dev_ops *ops; { register struct dev_opslist *dp; for (dp = dev_opslist; dp->devl_ops != NULL; dp = dp->devl_next) { if (dp->devl_ops == ops) return (EINVAL); } dp = (struct dev_opslist *) kmem_alloc(sizeof (*dp)); dp->devl_ops = ops; dp->devl_next = dev_opslist; dev_opslist = dp; return (0); } /* * Unattach any devices attached to this driver. */ void rem_drv(dev_ops) struct dev_ops *dev_ops; { void rem_a_device(); walk_devs(top_devinfo, (int (*)()) rem_a_device, (caddr_t) dev_ops); } /* * If this dev is linked to this driver, remove it. */ static void rem_a_device(dev, ops) struct dev_info *dev; struct dev_ops *ops; { if (dev->devi_driver == ops) dev->devi_driver = NULL; } /* * Remove a driver from the list of ops vectors. */ int rem_ops(ops) struct dev_ops *ops; { register struct dev_opslist *dp; register struct dev_opslist *prev = NULL; for (dp = dev_opslist; dp->devl_ops != NULL; prev = dp, dp = dp->devl_next) if (dp->devl_ops == ops) { if (prev == NULL) dev_opslist = dp->devl_next; else prev->devl_next = dp->devl_next; (void) kmem_free((caddr_t) dp, sizeof (*dp)); return (0); } return (EINVAL); } #endif VDDRV /* * This general-purpose routine walks down the tree of devices, calling * the given function for each one it finds, and passing the pointer arg * which can point to a structure of information that the function * needs. * It is useful when searches need to be made. * It needs to walk all devices, attached or not, as it is used before * any devices are attached. The function called should check if the * device is attached if it cares about such things. * It does the walk a layer at a time, not depth-first. */ walk_devs(dev_info, f, arg) struct dev_info *dev_info; int (*f)(); caddr_t arg; { register struct dev_info *dev; /* * Call the function for all the devices on this layer. * Then, for each device that has a slave, call walk_devs on * the slave. */ walk_layer(dev_info, f, arg); for (dev = dev_info; dev != (struct dev_info *)NULL; dev = dev->devi_next) { if (dev->devi_slaves) walk_devs(dev->devi_slaves, f, arg); } } /* * This general-purpose routine walks one layer of the dev_info tree, * calling the given function for each dev_info it finds, and passing * the pointer arg which can point to a structure of information that * the function needs. * It is useful when searches need to be made. * It walks all devices, attached or not. The function called should * check if the device is attached if it cares about such things. */ walk_layer(dev_info, f, arg) struct dev_info *dev_info; int (*f)(); caddr_t arg; { register struct dev_info *dev; /* * Call the function for this dev_info. * Go to the next device. */ for (dev = dev_info; dev != (struct dev_info *)NULL; dev = dev->devi_next) { (*f)(dev, arg); } } /* * This general-purpose routine walks one layer of the dev_info tree, * calling the given function for each dev_info that has slaves, passing * it the slave layer and the pointer arg which can point to a * structure of information that the function needs. * It is useful when searches need to be made. * It needs to walk all devices, attached or not, as it is used before * any devices are attached. The function called should check if the * device is attached if it cares about such things. */ walk_slaves(dev_info, f, arg) struct dev_info *dev_info; int (*f)(); caddr_t arg; { register struct dev_info *dev; /* * For each device on this layer that has a slave, call the * function on that slave layer. */ for (dev = dev_info; dev != (struct dev_info *)NULL; dev = dev->devi_next) { if (dev->devi_slaves) (*f)(dev->devi_slaves, arg); } } /* * Given a physical address, a bus type, and a size, return a virtual * address that maps the registers. * Returns NULL on any error. * NOTE: for Sun-4M, the allowable values for "space" are 0..15 * specifying the upper four bits of the 36-bit physical address, * or SPO_VIRTUAL. Other values will result in an error. */ addr_t map_regs(addr, size, space) addr_t addr; u_int size; int space; { register addr_t reg = NULL; u_int pageval; int s; int offset = (int)addr & PGOFSET; long a = 0; int extent; if (space == SPO_VIRTUAL) return addr; else if (space < 16) pageval = btop(addr) + (btop(space<<28)<<4); else return 0; extent = btoc(size + offset); if (extent == 0) extent = 1; s = splhigh(); a = rmalloc(kernelmap, (long)extent); (void)splx(s); if (a == 0) panic("out of kernelmap for devices"); reg = (addr_t)((int)kmxtob(a) | offset); segkmem_mapin(&kseg, (addr_t)((int)reg&PAGEMASK), (u_int)mmu_ptob(extent), PROT_READ | PROT_WRITE, pageval, 0); return (reg); } /* * Supposedly, this routine is the only place that enumerates details * of the encoding of the "bustype" part of the physical address. * This routine depends on details of the physical address map, and * will probably be somewhat different for different machines. * * Future implementations of Sun-4M machines may do this differently. * * Sun-4M treats space and addr as the upper and lower 32 bits * of a 64-bit physical address; SP0_VIRTUAL is special-cased * as noting that this is a 'virtual' connection. */ char ukbuf[] = "space #"; decode_address(space, addr) unsigned space, addr; { char *name; #ifdef VME struct vme_spaces *vp; #endif VME #if defined(SUN4M_35) extern int swift; #endif if (space == SPO_VIRTUAL) /* weird name */ name = "virtual"; else { ukbuf[6] = "0123456789ABCDEF"[space & 15]; name = ukbuf; switch(cpu) { #if defined(SUN4M_690) || defined(SUN4M_50) case CPU_SUN4M_690: case CPU_SUN4M_50: switch(space) { case 0x0: name = "obmem"; break; case 0xE: name = "SBus slot #"; name[10] = "0123456789abcdef"[addr>>28]; addr &= 0x0FFFFFFF; break; case 0xF: /* XXX - magic numbers are ugly. */ if (addr < 0xF1000000) name = "sys"; else { name = "obio"; addr -= 0xF1000000; } break; } #ifdef VME for (vp = vme_spaces; vp->name; ++vp) if ((vp->space == space) && (vp->base <= addr)) { name = vp->name; addr -= vp->base; break; } #endif VME break; #endif (SUN4M_690 || SUN4M_50) #if defined(SUN4M_35) /* tsunami / swift */ case CPU_SUN4M_35: ukbuf[6] = "01234567"[addr >> 28]; switch(addr >> 28) { case 0x0: name = "obmem"; break; case 0x1: name = "obio"; break; case 0x2: if (!swift) break; case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: name = "SBus slot #"; name[10] = "012345"[(addr>>28) - (swift ? 2 : 3)]; break; } addr &= 0x0FFFFFFF; break; #endif (SUN4M_35) default: break; } } printf(" %s 0x%x", name, addr); } /* * Compute the address of an I/O device within standard address * ranges and return the result. This is used by DKIOCINFO * ioctl to get the best guess possible for the actual address * the card is at. */ getdevaddr(addr) caddr_t addr; { int off = (int)addr & MMU_PAGEOFFSET; struct pte pte; int paddr, pageno, space; #ifdef VME struct vme_spaces *vp; #endif VME mmu_getkpte(addr, &pte); pageno = pte.PhysicalPageNumber; paddr = mmu_ptob(pageno); space = pageno >> 20; switch(cpu) { #if defined(SUN4M_690) || defined(SUN4M_50) case CPU_SUN4M_690: case CPU_SUN4M_50: if ((pageno >= 0xFF1000) && (pageno < 0xFF2000)) { paddr -= 0xF1000000; break; } #ifdef VME for (vp = vme_spaces; vp->name; ++vp) if ((vp->space == space) && (vp->base <= paddr)) { paddr -= vp->base; break; } #endif VME break; #endif #if defined(SUN4M_35) case CPU_SUN4M_35: /* offset from SBus base */ switch((paddr >> 28) & 7) { case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: paddr -= 0x30000000; break; } break; #endif defined(SUN4M_35) default: break; } return (paddr + off); } /* * figure out what type of bus a page frame number points at. * NOTE: returns a BT_ macro, not the top four bits. most * things called "bustype" are actually just the top four * bits of the pte, which are part of the physical address * space as defined in the archetecture and which change between * various Sun-4m models. */ bustype(pageno) int pageno; { unsigned space = pageno >> (32 - MMU_PAGESHIFT); /* %%% */ switch(cpu) { #if defined(SUN4M_690) case CPU_SUN4M_690: switch(space) { case 0x0: return BT_OBMEM; case 0x9: return BT_VIDEO; case 0xA: case 0xB: case 0xC: case 0xD: return BT_VME; /* do we need more detail? */ case 0xE: return BT_SBUS; case 0xF: return BT_OBIO; } break; #endif #if defined(SUN4M_50) case CPU_SUN4M_50: /* campus 2 */ switch(space) { case 0x0: return BT_OBMEM; case 0x9: return BT_VIDEO; case 0xE: return BT_SBUS; case 0xF: return BT_OBIO; } break; #endif #if defined(SUN4M_35) case CPU_SUN4M_35: space = pageno >> (28 - MMU_PAGESHIFT); switch(space) { case 0x0: return BT_OBMEM; case 0x1: return BT_OBIO; case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: return BT_SBUS; } break; #endif default: break; } return BT_UNKNOWN; } #ifdef OLD /* * Say that a device is here */ void report_dev(dev) register struct dev_info *dev; { int i, pri; register int nregs = dev->devi_nreg; register int nintr = dev->devi_nintr; /* char *ty; */ /* Not used anywhere */ printf("%s%d", dev->devi_name, dev->devi_unit); for (i = 0; i < nregs; i++) { if (i == 0) printf(" at"); else printf(" and"); decode_address((unsigned) dev->devi_reg[i].reg_bustype, (unsigned) dev->devi_reg[i].reg_addr); } for (i = 0; i < nintr; i++) { if (i == 0) printf(" "); else printf(", "); /* ty = ""; */ /* Don't know why this is useful */ pri = LPL2SPL(dev->devi_intr[i].int_pri); printf("pri %d", pri); switch (dev->devi_intr[i].int_pri & ~15) { case INTLEVEL_SOFT: printf(" (soft)"); break; case INTLEVEL_ONBOARD: printf(" (onboard)"); break; case INTLEVEL_SBUS: printf(" (sbus level %d)", LPL2SVL(dev->devi_intr[i].int_pri)); break; #ifdef VME case INTLEVEL_VME: printf(" (vme level %d)", LPL2SVL(dev->devi_intr[i].int_pri)); break; #endif VME } if (dev->devi_intr[i].int_vec) printf(" vec 0x%x", dev->devi_intr[i].int_vec); } printf("\n"); } #endif /* * Unmap the virtual address range from addr to addr+size. * panics on any error (but at a lower level). */ void unmap_regs(addr, size) addr_t addr; u_int size; { long a; int extent; int offset; int s; offset = (int)addr & PGOFSET; extent = btoc(size + offset); if (extent == 0) extent = 1; segkmem_mapout(&kseg, (addr_t)((int)addr&PAGEMASK), (u_int)mmu_ptob(extent)); a=btokmx(addr); s = splhigh(); rmfree(kernelmap, (long)extent, (u_long)a); (void)splx(s); } /* * This routine returns the length of the property value for the named * property. */ int getproplen(id, name) int id; char *name; { return prom_getproplen((dnode_t) id, (caddr_t) name); } /* * This routine allows drivers to easily retrieve the value of a property * for int-sized values. The value of 'name' in the property list for 'devi' * is returned. If no value is found, 'def' is returned. * * As a special case, if a zero length property is found, 1 is returned. */ int getprop(id, name, def) int id; char *name; int def; { int value; switch (getproplen(id, name)) { case 0: return (1); case sizeof (int): (void) prom_getprop((dnode_t)id, name, (caddr_t) &value); return (value); case -1: default: return (def); } /*NOTREACHED*/ } /* * This routine allows drivers to easily retrieve the value of a property * for arbitrary sized properties. A pointer to the value of 'name' in * the property list for 'devi' is returned. If no value is found, NULL * is returned. The space is allocated using kmem_zalloc, so it is assumed * that this routine is NOT called from an interrupt routine. */ addr_t getlongprop(id, name) int id; char *name; { int size; addr_t value; size = getproplen(id, name); if (size <= 0) return (NULL); value = kmem_zalloc((u_int) size); if (value == NULL) { #ifdef MULTIPROCESSOR mpprom_eprintf("getlongprop: unable to allocate %d bytes\n", size); #endif MULTIPROCESSOR panic("getlongprop: kmem_zalloc failed"); } (void)prom_getprop((dnode_t) id, name, value); return (value); } /* * Spurious interrupt messages * * Probably better if the scanning code calls * not_serviced manually if nothing in the list * mananged to handle the interrupt, so we * don't call it by mistake when servicing * soft interrupts (which MUST call everything * in the entire list). */ #define SPURIOUS -1 /* recognized in locore.s */ int not_serviced(counter, level, bus) int *counter, level; char *bus; { if ((*counter)++ % 100 == 1) printf("Level %d %sinterrupt not serviced\n", level, bus); return (SPURIOUS); } char busname_ovec[] = "onboard "; char busname_svec[] = "SBus "; char busname_vvec[] = "VME "; char busname_vec[] = ""; #define OVECTOR(n) \ int olvl/**/n/**/_spurious; \ struct autovec olvl/**/n[NVECT] #define SVECTOR(n) \ int slvl/**/n/**/_spurious; \ struct autovec slvl/**/n[NVECT] #define VVECTOR(n) \ int vlvl/**/n/**/_spurious; \ struct autovec vlvl/**/n[NVECT] #define XVECTOR(n) \ struct autovec xlvl/**/n[NVECT] #define VECTOR(n) \ int level/**/n/**/_spurious; \ struct autovec level/**/n[NVECT] typedef int (*func)(); extern int softint(); extern int process_aflt(); extern int hardlevel10(); /* * These structures are used in locore.s to jump to device interrupt routines. * They also provide vmstat assistance. * They will index into the string table generated by autoconfig * but in the exact order addintr sees them. This allows IOINTR to quickly * find the right counter to increment. * (We use the fact that the arrays are initialized to 0 by default). */ /* * Initial interrupt vector information. * Each of these macros defines both the "spurious-int" counter and * the list of autovec structures that will be used by locore.s * to distribute interrupts to the interrupt requestors. * Each list is terminated by a null. * Lists are scanned only as needed: hard ints * stop scanning when the int is claimed; soft ints * scan the entire list. If nobody on the list claims the * interrupt, then a spurious interrupt is reported. * * These should all be initialized to zero, except for the * few interrupts that we have handlers for built into the * kernel that are not installed by calling "addintr". * I would like to eventually get everything going through * the "addintr" path. * It might be a good idea to remove VECTORs that are not * actually processed by locore.s */ /* onboard interrupts */ /* levels 1..13: lock acquired before calling service */ OVECTOR(1) = {{0}}; OVECTOR(2) = {{0}}; OVECTOR(3) = {{0}}; OVECTOR(4) = {{0}}; /* scsi */ OVECTOR(5) = {{0}}; OVECTOR(6) = {{0}}; /* ethernet */ OVECTOR(7) = {{0}}; OVECTOR(8) = {{0}}; /* video */ OVECTOR(9) = {{0}}; /* module int */ OVECTOR(10) = {{0}}; /* system timer */ OVECTOR(11) = {{0}}; OVECTOR(12) = {{0}}; /* serial ports */ OVECTOR(13) = {{0}}; /* levels 14..15: lock not entered */ OVECTOR(14) = {{0}}; /* processor timer */ OVECTOR(15) = {{0}}; /* async errors */ /* SBus Interrupts */ /* all levels: lock held before calling service routines */ SVECTOR(1) = {{0}}; SVECTOR(2) = {{0}}; SVECTOR(3) = {{0}}; SVECTOR(4) = {{0}}; SVECTOR(5) = {{0}}; SVECTOR(6) = {{0}}; SVECTOR(7) = {{0}}; /* VME Interrupts */ /* all levels: lock held before calling service routines */ VVECTOR(1) = {{0}}; VVECTOR(2) = {{0}}; VVECTOR(3) = {{0}}; VVECTOR(4) = {{0}}; VVECTOR(5) = {{0}}; VVECTOR(6) = {{0}}; VVECTOR(7) = {{0}}; /* software vectored interrupts */ /* levels 1..9: lock held before calling service routines */ XVECTOR(1) = {{softint},{0}}; /* time-scheduled tasks */ XVECTOR(2) = {{0}}; XVECTOR(3) = {{0}}; XVECTOR(4) = {{0}}; XVECTOR(5) = {{0}}; XVECTOR(6) = {{0}}; XVECTOR(7) = {{0}}; XVECTOR(8) = {{0}}; XVECTOR(9) = {{0}}; /* levels 10..13: forwarding is responsibility of service routine */ XVECTOR(10) = {{0}}; XVECTOR(11) = {{0}}; XVECTOR(12) = {{process_aflt},{0}}; /* process async. fault */ XVECTOR(13) = {{0}}; XVECTOR(14) = {{0}}; XVECTOR(15) = {{0}}; /* otherwise unclaimed sparc interrupts */ /* levels 1..13: lock held before calling service routine */ VECTOR(1) = {{0}}; VECTOR(2) = {{0}}; VECTOR(3) = {{0}}; VECTOR(4) = {{0}}; VECTOR(5) = {{0}}; VECTOR(6) = {{0}}; VECTOR(7) = {{0}}; VECTOR(8) = {{0}}; VECTOR(9) = {{0}}; VECTOR(10) = {{0}}; VECTOR(11) = {{0}}; VECTOR(12) = {{0}}; VECTOR(13) = {{0}}; /* levels 14..15: lock not entered. */ VECTOR(14) = {{0}}; VECTOR(15) = {{0}}; /* * indirection table, to save us some large switch statements * NOTE: This must agree with "INTLEVEL_foo" constants in * */ struct autovec *vectorlist[] = { /* * otherwise unidentified interrupts at SPARC levels 1..15 */ 0, level1, level2, level3, level4, level5, level6, level7, level8, level9, level10,level11,level12,level13,level14,level15, /* * interrupts identified as "soft" */ 0, xlvl1, xlvl2, xlvl3, xlvl4, xlvl5, xlvl6, xlvl7, xlvl8, xlvl9, xlvl10, xlvl11, xlvl12, xlvl13, xlvl14, xlvl15, /* * interrupts identified as "onboard" */ 0, olvl1, olvl2, olvl3, olvl4, olvl5, olvl6, olvl7, olvl8, olvl9, olvl10, olvl11, olvl12, olvl13, olvl14, olvl15, /* * interrupts identified as "sbus" -- note that sbus has only level 1 * through 7; these are mapped to the proper sparc levels via the * boot prom forth word "???". */ 0, 0, slvl1, slvl2, 0, slvl3, 0, slvl4, 0, slvl5, 0, slvl6, 0, slvl7, 0, 0, /* * interrupts identified as "vme" -- note that vme has only level 1 * through 7; these are mapped to the proper sparc levels via the * boot prom forth word "???". */ 0, 0, vlvl1, vlvl2, 0, vlvl3, 0, vlvl4, 0, vlvl5, 0, vlvl6, 0, vlvl7, 0, 0, }; #define MAXAUTOVEC (sizeof vectorlist / sizeof vectorlist[0]) /* * vmstat -i support for autovectored interrupts. * add the autovectored interrupt to the table of mappings of * interrupt counts to device names. We can't do much about * a device that has multiple units, so we print '?' for such. * Otherwise, we try to use the real unit number. */ #ifndef oldstuff /*ARGSUSED */ #endif oldstuff void addioname(levelp, name, unit) register struct autovec *levelp; char *name; int unit; { if (levelp->av_name == NULL) levelp->av_name = name; else if (strcmp(levelp->av_name, name)) { printf("addioname: can't change %s to %s!\n", levelp->av_name, name); panic("addioname"); } levelp->av_devcnt++; } /* * Arrange for a driver to be called when a particular * auto-vectored interrupt occurs. * NOTE: if a device can generate interrupts on more than * one level, or if a driver services devices that interrupt * on more than one level, then the driver should install * itself on each of those levels. * devices may also install themselves on the VME, Internal, or SBUS * vector lists using inerrupt level offsets, see . * On Hard-ints, order of evaluation of the chains is: * if onboard int at this level active in SIPR, * scan "onboard" chain; if nobody claims, * remember bus-type is onboard. * if sbus int at this level active in SIPR, * scan "sbus" chain; if nobody claims, * remember bus-type is sbus. * if vme int at this level active in SIPR, * scan "vme" chain; if nobody claims, * remember bus-type is vme. * scan "unspecified" chain; if nobody claims, * report spurious interrupt and remembered bus-type if any. * Scanning terminates with the first driver that claims it has * serviced the interrupt. * * On Soft-ints, order of evaulation of the chains is: * scan the "soft" chain * scan the "unspecified" chain * Scanning continues until all service routines have been called. * If nobody claims the interrupt, report a spurious soft interrupt. */ addintr(lvl, xxintr, name, unit) func xxintr; char *name; int unit; { register func f; register struct autovec *levelp; register int i; if ((f = xxintr) == NULL) return; if (lvl >= MAXAUTOVEC) panic("addintr: vector number out of range"); levelp = vectorlist[lvl]; if (levelp == (struct autovec *)0) panic("addintr: specified vector can not be polled"); /* If devcnt == -1, settrap() claimed this trap */ if (levelp->av_devcnt == -1) { /* XXX - addintr() should return success/failure */ panic("addintr: interrupt trap vector busy\n"); } for (i = 0; i < NVECT; i++) { if (levelp->av_vector == NULL) { /* found an open slot */ bzero((caddr_t) levelp, sizeof (*levelp)); levelp->av_vector = f; /* add f to list */ addioname(levelp, name, unit); return; } if (levelp->av_vector == f) { /* already in list */ addioname(levelp, name, unit); return; } levelp++; } panic("addintr: too many devices"); } /* * Opcodes for instructions in scb entries */ #define MOVPSRL0 0xa1480000 /* mov %psr, %l0 */ #define MOVL4 0xa8102000 /* mov (+/-0xfff), %l4 */ #define MOVL6 0xac102000 /* mov (+/-0xfff), %l6 */ #define BRANCH 0x10800000 /* ba (+/-0x1fffff)*4 */ #define SETHIL3 0x27000000 /* sethi %hi(0xfffffc00), %l3 */ #define JMPL3 0x81c4e000 /* jmp %l3+%lo(0x3ff) */ #define NO_OP 0x01000000 /* nop */ #define BRANCH_RANGE 0x001fffff /* max branch displacement */ #define BRANCH_MASK 0x003fffff /* branch displacement mask */ #define SETHI_HI(x) (((x) >> 10) & 0x003fffff) /* %hi(x) */ #define JMP_LO(x) ((x) & 0x000003ff) /* %lo(x) */ /* * Dynamically set a hard trap vector in the scb table. * This routine allows device drivers, such as fd and audio_79C30, * to dynamically configure assembly-language interrupt handlers. * If 'xxintr' is NULL, the trap vector is set back to the default system trap. * * If this trap can be autovectored, set the av_devcnt field to -1 when a * direct trap vector is set, so that two devices can't claim the same * interrupt. If av_devcnt > 0, this trap is already autovectored, so don't * give the trap vector away. * * For sun4m machines interrupt level assignments are handled differently. * (see ../sun/autoconf.h for details). As such the interrupt level passed * to settrap "lvl" is INTLEVEL_XXX+n. Where n is the normal level (1..15) * and XXX represent one of (SOFT, ONBOARD, SBUS, VME). This logical * interrupt number gets translated into the real SPARC level to select * which interrupt vector will be modified. * * Return 0 if the trap vector could not be set according to the request. * Otherwise, return 1. */ int settrap(lvl, xxintr) int lvl; func xxintr; { struct autovec *levelp; trapvec vec; extern sys_trap(); extern int nwindows; /* number of register windows */ if (lvl >= MAXAUTOVEC) panic("settrap: vector number out of range\n"); lvl = LPL2SPL(lvl); if ((lvl == 10) || /* reserved for system clock */ (lvl == 12) || /* reserved for serial ports */ (lvl == 14) || /* reserved for monitor clock */ (lvl == 15)) { /* reserved for internal sys communication */ printf("settrap: cannot set trap for processor level %d\n", lvl); return(0); } levelp = vectorlist[lvl]; if (!levelp) { printf("settrap: cannot set trap for processor level %d\n", lvl); return(0); } /* * Check to see whether trap is available. * If devcnt == 0, this is a system trap with no autovectors. * If devcnt > 0, this is a system trap with autovectors set. * If devcnt == -1, this is a direct trap (addintr() will complain). */ if (levelp != NULL) { if (xxintr != NULL) { /* If trying to set a direct trap handler */ if (levelp->av_devcnt == -1) { printf("settrap: level %d trap busy\n", lvl); return (0); } else if (levelp->av_devcnt != 0) { printf("settrap: level %d trap is autovectored\n", lvl); return (0); } levelp->av_devcnt = -1; /* don't autovector */ } else { /* Setting back to system trap */ levelp->av_devcnt = 0; } } if (xxintr == NULL) { /* Reset the trap vector to a default system trap */ vec.instr[0] = MOVPSRL0; vec.instr[1] = SETHIL3 | SETHI_HI((int)sys_trap); vec.instr[2] = JMPL3 | JMP_LO((int)sys_trap); vec.instr[3] = MOVL4 | T_INTERRUPT | lvl; } else { vec.instr[0] = MOVPSRL0; vec.instr[1] = SETHIL3 | SETHI_HI((int)xxintr); vec.instr[2] = JMPL3 | JMP_LO((int)xxintr); vec.instr[3] = MOVL6 | (nwindows - 1); } write_scb_int(lvl, &vec); /* set the new vector */ return (1); } /* * Increase splvm_val, if necessary, for a device that uses DMA. * Level is usually dev->devi_intr->int_pri. * * XXX keep SPLMB in step. */ adddma(level) int level; { int spl; spl = ipltospl(LPL2SPL(level)); if (spl > splvm_val) { splvm_val = spl; SPLMB = spltopri(spl); /* * note: splr(pritospl(SPLMB)) might be one level * higher than splvm(). This is important if we * get an adddma for SPARC Level-9, where the splr * would lock out the clock at Level-10 as well. */ } } /* * store dk name && unit number info * reuse empty slots, if possible (loadable drivers, or * the case of drives coming and going off line) */ int newdk(name, unit) char *name; int unit; { int i; int j = -1; for (i = 0; i < dkn; i++) { char *p; if ((p = dk_ivec[i].dk_name) == NULL || *p == '\0') { j = i; break; } } if (j < 0) { if (dkn >= DK_NDRIVE) { return (-1); } else { j = dkn++; } } dk_ivec[j].dk_name = name; dk_ivec[j].dk_unit = unit; return (j); } /* * remove dk name && unit number info */ int remdk(num) int num; { if (num >= dkn || num < 0) return (-1); else { dk_ivec[num].dk_name = ""; dk_ivec[num].dk_unit = 0; dk_time[num] = dk_seek[num] = dk_xfer[num] = dk_wds[num] = dk_bps[num] = dk_read[num] = 0; /* free up the last one */ if ((num+1) == dkn) dkn = num; return (0); } } #ifdef VDDRV /* * These routines are the inverse of the previous three routines; * they allow a driver to be unloaded. */ /* * remove the autovectored interrupt from the table of mappings of * interrupt counts to device names. */ /*ARGSUSED*/ void remioname(levelp) struct autovec *levelp; { /* * We could try to remove the name from the av_nametab list, but * that wouldn't buy us much. We're about to overwrite this * entry anyway, so there is nothing to do here. * So do nothing. */ } /* * Remove a driver from the autovector list. * NOTE: if a driver has installed itself on more than one level, * then the driver should remove itself from each of those levels. */ remintr(lvl, xxintr) func xxintr; { register func f; register struct autovec *levelp; register int i; if ((f = xxintr) == NULL) return (0); if (lvl >= MAXAUTOVEC) panic("remintr: vector number out of range"); levelp = vectorlist[lvl]; if (levelp == (struct autovec *)0) panic("remintr: specified vector can not be polled"); for (i = 0; i < NVECT; i++, levelp++) { if (levelp->av_vector == NULL) { /* end of list reached */ break; } if (levelp->av_vector == f) { /* found it */ remioname(levelp); /* move them all down one */ for (i++; i < NVECT; i++, levelp++) levelp[0] = levelp[1]; return (0); } } /* one way or another, it isn't in the list */ printf("remintr: driver not installed on level %d\n", lvl); return (-1); } #endif VDDRV /* * Configure swap space and related parameters. */ swapconf() { #ifndef SAS register struct bootobj *swp; register int nblks; struct vattr vattr; struct vfs *vfsp; struct vfssw *vsw; int error; extern char *strcpy(); extern u_int swapwarn; /* from param.c */ swp = &swapfile; error = ENODEV; vfsp = NULL; /* * No filesystem type specified, try to get default */ if (((boothowto & RB_ASKNAME) || (*(swp->bo_fstype) == '\0')) && (vsw = getfstype("swap", swp->bo_fstype))) { (void) strcpy(swp->bo_fstype, vsw->vsw_name); vfsp = (struct vfs *)kmem_alloc(sizeof (*vfsp)); VFS_INIT(vfsp, vsw->vsw_ops, 0); error = VFS_SWAPVP(vfsp, &swp->bo_vp, swp->bo_name); } else if (*swp->bo_name == '/') { /* * file name begins with a slash, try to look it up * as a pathname starting at the root. */ error = lookupname(swp->bo_name, UIO_SYSSPACE, FOLLOW_LINK, (struct vnode **)0, &swp->bo_vp); printf("swapconf: looked up %s, got error %d\n", swp->bo_name, error); if (!error) { (void) strcpy(swp->bo_fstype, rootfs.bo_fstype); } } else { /* * Look through all supported filesystem types for one * that matches the type of swp, or if swp has no * fstype just take the first filesystem type that * will supply a swap vp */ for (vsw = &vfssw[0]; vsw <= &vfssw[MOUNT_MAXTYPE]; vsw++) { if (vsw->vsw_name == 0) { continue; } if (*(swp->bo_fstype) == '\0' || (strcmp(swp->bo_fstype, vsw->vsw_name) == 0)) { vfsp = (struct vfs *)kmem_alloc(sizeof (*vfsp)); VFS_INIT(vfsp, vsw->vsw_ops, 0); error = VFS_SWAPVP(vfsp, &swp->bo_vp, swp->bo_name); if (error == 0) { break; } } } } if (error || swp->bo_vp == NULL) { if (error == ENODEV) printf("swap device '%s': Not Present\n", swp->bo_name); else printf("bad swap device '%s': error %d\n", swp->bo_name, error); bad: if (swp->bo_vp) { VN_RELE(swp->bo_vp); swp->bo_vp = NULL; } if (vfsp) { kmem_free((char *)vfsp, sizeof (*vfsp)); } if (error) return; } else { error = VOP_OPEN(&swp->bo_vp, FREAD|FWRITE, u.u_cred); if (error) { printf("error %d in opening swap file %s\n", error, swp->bo_name); goto bad; } error = VOP_GETATTR(swp->bo_vp, &vattr, u.u_cred); if (error) { printf("error %d getting size of swapfile %s\n", error, swp->bo_name); goto bad; } nblks = btodb(vattr.va_size); if (swp->bo_size == 0 || swp->bo_size > nblks) swp->bo_size = nblks; if (*swp->bo_fstype == '\0') { (void) strcpy(swp->bo_fstype, vsw->vsw_name); } #ifdef VFSSTATS if (vfs_add(swp->bo_vp, vfsp, 0) == 0 && vfsp->vfs_stats == 0) { struct vfsstats *vs; vs = (struct vfsstats *) kmem_zalloc(sizeof (struct vfsstats)); vs->vs_time = time.tv_sec; vfsp->vfs_stats = (caddr_t)vs; } #endif printf("swap on %s fstype %s size %dK\n", swp->bo_name, swp->bo_fstype, dbtob(swp->bo_size) / 1024); if (nblks < swapwarn) printf("WARNING: swap space probably too small\n"); swp->bo_flags |= BO_VALID; swap_present = 1; } #else SAS register struct bootobj *swp; struct vattr vattr; int error; swp = &swapfile; if (strcmp(swp->bo_name, "sim0") != 0) { printf("bad swap name '%s', should be 'sim0'\n", swp->bo_name); panic("bad swap device"); } /* * Previously was making the call, makespecvp(makedev(1, 1), VBLK), instead of * making the call, bdevvp(makedev(1, 1)). Calling bdevvp is what the non-SAS * call to VFS_SWAPVP (see above) essentially does, so it is the right thing for * SAS to do simulate the real world as close as possible, given the way the sim0 * device is currently implemented. Calling makespecvp would result in it first * creating a shadow snode (and vnode contained inside), then creating the real * vnode via calling bdevvp, and having the s_bdevvp field of the shadow snode * point to this real vnode. The problem with this approach is that when the * shaddow vnode is passed to spec_getpage to perform a swapin, spec_getpage is * expecting the real vnode, and panics when VTOS(vp)->b_devvp != vp. */ swp->bo_vp = bdevvp(makedev(1, 1)); error = VOP_OPEN(&swp->bo_vp, FREAD+FWRITE, u.u_cred); if (error) { printf("error %d opening swap\n", error); panic("cannot open swap device"); } error = VOP_GETATTR(swp->bo_vp, &vattr, u.u_cred); if (error) { printf("error %d getting size of swap\n", error); panic("cannot get swap size"); } swp->bo_size = btodb(vattr.va_size); if (*swp->bo_fstype == '\0') { (void) strcpy(swp->bo_fstype, "spec"); } printf("swap on %s fstype %s size %dK\n", swp->bo_name, swp->bo_fstype, dbtob(swp->bo_size) / 1024); swp->bo_flags |= BO_VALID; swap_present = 1; /* main checks for this */ #endif SAS } dev_t kbddev = NODEV; dev_t mousedev = NODEV; #define NOUNIT -1 #ifdef SAS #define SIMCMAJOR 12 /* simulated console */ #else !SAS #define WSCONSMAJOR 1 /* "workstation console" */ #define ZSMAJOR 12 /* serial */ #define KBDMINOR 0 #define MOUSEMINOR 1 #define CONSKBDMAJOR 29 /* console keyboard driver */ #define CONSMOUSEMAJOR 13 /* console mouse driver */ #endif !SAS extern struct fileops vnodefops; struct consinfo { int kmunit; /* keyboard/mouse unit number */ dev_t fbdev; /* frame buffer device */ struct dev_info *options; /* options dev_info (for keyclick et al.) */ }; /* * Configure keyboard, mouse, and frame buffer using * monitor provided values * configuration flags * N.B. some console devices are statically initialized by the * drivers to NODEV. */ consconfig() { int e; #ifndef SAS struct consinfo consinfo; struct termios termios; int kbdtranslatable = TR_CANNOT; int kbdspeed = B9600; struct vnode *kbdvp; struct vnode *conskbdvp; struct file *fp; int i; char devname[OBP_MAXDEVNAME]; int unit; int findcons(/* (struct dev_info *), (struct consinfo *) */); int zsminor; char *p; extern char *prom_get_stdin_subunit(); stop_mon_clock(); /* turn off monitor polling clock */ consinfo.kmunit = NOUNIT; consinfo.fbdev = NODEV; consinfo.options = NULL; /* * check for console on same ascii port to allow full speed * output by using the UNIX driver and avoiding the monitor. */ if (prom_stdin_stdout_equivalence() == 0) goto namedone; if (prom_get_stdin_dev_name(devname, sizeof devname) != 0) goto namedone; if (strcmp("zs", devname) != 0) goto namedone; if ((unit = prom_get_stdin_unit()) == -1) goto namedone; zsminor = 0; if ((p = prom_get_stdin_subunit()) != (char *)0) if ((*p) != (char)0) zsminor = (*p - 'a') & 1; zsminor = (unit << 1) + zsminor; rconsdev = makedev(ZSMAJOR, zsminor); #else SAS rconsdev = makedev(SIMCMAJOR, 0); #endif SAS namedone: if (rconsdev) { /* * Console is a CPU serial port. */ rconsvp = makespecvp(rconsdev, VCHR); kbddev = rconsdev; /* * Opening causes interrupts, etc. to be initialized. * Console device drivers must be able to do output * after being closed! */ if (e = VOP_OPEN(&rconsvp, FREAD|FWRITE|FNOCTTY|FNDELAY, u.u_cred)) printf("console open failed: error %d\n", e); /* now we must close it to make console logins happy */ VOP_CLOSE(rconsvp, FREAD|FWRITE, 1, u.u_cred); consdev = rconsdev; consvp = rconsvp; #ifndef SAS /* Still need to find options dev_info */ walk_devs(top_devinfo, findcons, (char *) &consinfo); options_devinfo = consinfo.options; set_keyclick(options_devinfo); #endif /* SAS */ return; } #ifndef SAS /* * The following code assumes mappings for ZS minor devices: * * ttya/zs devinfo unit 0/Channel a --> zs minor device #0 * ttyb/zs devinfo unit 0/Channel b --> zs minor device #1 * * See comments concerning zs minor dev mappings in zs_async.c */ if (prom_stdin_is_keyboard()) goto kbddone; if (prom_get_stdin_dev_name(devname, sizeof devname) != 0) goto kbddone; if (strcmp("zs", devname) != 0) goto kbddone; if ((unit = prom_get_stdin_unit()) != -1) { zsminor = 0; if ((p = prom_get_stdin_subunit()) != (char *)0) if (*p != (char)0) zsminor = (*p - 'a') & 1; zsminor = (unit << 1) + zsminor; kbddev = makedev(ZSMAJOR, zsminor); } kbddone: if (kbddev != NODEV) kbdspeed = zsgetspeed(kbddev); /* * Look for the [last] kbd/ms and matching fbtype. */ walk_devs(top_devinfo, findcons, (char *) &consinfo); options_devinfo = consinfo.options; set_keyclick(options_devinfo); /* * Use serial keyboard and mouse if found flagged uart */ if (consinfo.kmunit != NOUNIT) { if (mousedev == NODEV) mousedev = makedev(ZSMAJOR, consinfo.kmunit * 2 + MOUSEMINOR); if (kbddev == NODEV) { kbddev = makedev(ZSMAJOR, consinfo.kmunit * 2 + KBDMINOR); kbdtranslatable = TR_CAN; } } if (kbddev == NODEV) panic("no keyboard found"); /* * Open the keyboard device. */ kbdvp = makespecvp(kbddev, VCHR); if (e = VOP_OPEN(&kbdvp, FREAD|FNOCTTY, u.u_cred)) { printf("keyboard open failed: error %d\n", e); panic("can't open keyboard"); } e = FLUSHRW; /* Flush both read and write */ strioctl(kbdvp, I_FLUSH, (caddr_t)&e, FREAD); u.u_error = 0; /* Just In Case */ if (!ldreplace(kbdvp, kbdvp->v_stream, KBDLDISC)) { u.u_error = EINVAL; } if (u.u_error != 0) { printf("consconfig: can't set line discipline: error %d\n", u.u_error); panic("can't set up keyboard"); } strioctl(kbdvp, KIOCTRANSABLE, (caddr_t)&kbdtranslatable, 0); if (kbdtranslatable == TR_CANNOT) { /* * For benefit of serial port keyboard. */ strioctl(kbdvp, TCGETS, (caddr_t)&termios, FREAD); termios.c_cflag &= ~(CBAUD|CIBAUD); termios.c_cflag |= kbdspeed; strioctl(kbdvp, TCSETSF, (caddr_t)&termios, FREAD); if (u.u_error != 0) { printf("consconfig: TCSETSF error %d\n", u.u_error); u.u_error = 0; } } /* * Open the "console keyboard" device, and link the keyboard * device under it. * XXX - there MUST be a better way to do this! */ conskbdvp = makespecvp(makedev(CONSKBDMAJOR, 1), VCHR); if (e = VOP_OPEN(&conskbdvp, FREAD|FWRITE|FNOCTTY, u.u_cred)) { printf("console keyboard device open failed: error %d\n", e); panic("can't open keyboard"); } if ((fp = falloc()) == NULL) panic("can't get file descriptor for keyboard"); i = u.u_r.r_val1; /* XXX - get FD number */ fp->f_flag = FREAD; fp->f_type = DTYPE_VNODE; fp->f_data = (caddr_t)kbdvp; fp->f_ops = &vnodefops; strioctl(conskbdvp, I_PLINK, (caddr_t)&i, FREAD); if (u.u_error != 0) { printf("I_PLINK failed: error %d\n", u.u_error); panic("can't link kbddev under /dev/kbd"); } u.u_ofile[i] = NULL; closef(fp); /* don't need this any more */ kbddevopen = 1; /* try to configure mouse */ if (mousedev != NODEV && mouseconfig(mousedev)) { #if !defined(MUNIX) && !defined(MINIROOT) printf("No mouse found\n"); #endif mousedev = NODEV; u.u_error = 0; } /* * Set up default frame buffer. */ if (fbdev == NODEV && (fbdev = consinfo.fbdev) == NODEV) { #if !defined(MUNIX) && !defined(MINIROOT) printf("No default frame buffer found\n"); #endif } /* * Open the "workstation console" device, and link the "console * keyboard" device under it. * XXX - there MUST be a better way to do this! */ rconsdev = makedev(WSCONSMAJOR, 0); rconsvp = makespecvp(rconsdev, VCHR); if (e = VOP_OPEN(&rconsvp, FREAD|FWRITE|FNOCTTY, u.u_cred)) { printf("console open failed: error %d\n", e); panic("can't open console"); } if ((fp = falloc()) == NULL) panic("can't get file descriptor for console keyboard"); i = u.u_r.r_val1; /* XXX - get FD number */ fp->f_flag = FREAD; fp->f_type = DTYPE_VNODE; fp->f_data = (caddr_t)conskbdvp; fp->f_ops = &vnodefops; strioctl(rconsvp, I_PLINK, (caddr_t)&i, FREAD); if (u.u_error != 0) { printf("consconfig: I_PLINK failed: error %d\n", u.u_error); panic("can't link /dev/kbd under workstation console"); } u.u_ofile[i] = NULL; closef(fp); /* don't need this any more */ /* now we must close it to make console logins happy */ VOP_CLOSE(rconsvp, FREAD|FWRITE, 1, u.u_cred); consdev = rconsdev; consvp = rconsvp; #else SAS panic("no keyboard found"); #endif SAS } #ifndef SAS static mouseconfig(msdev) dev_t msdev; { int e; int flushrw = FLUSHRW; struct vnode *mousevp; struct vnode *consmousevp; struct file *fp; int fd; /* Open the mouse device. */ mousevp = makespecvp(msdev, VCHR); if (e = VOP_OPEN(&mousevp, FREAD|FNOCTTY, u.u_cred)) return (e); strioctl(mousevp, I_FLUSH, (caddr_t) &flushrw, FREAD); u.u_error = e = 0; /* Just In Case */ if (!ldreplace(mousevp, mousevp->v_stream, MOUSELDISC)) e = EINVAL; if (e != 0) { (void) VOP_CLOSE(mousevp, FREAD|FWRITE, 1, u.u_cred); return (e); } /* * Open the "console mouse" device, and link the mouse device under it. * XXX - there MUST be a better way to do this! */ consmousevp = makespecvp(makedev(CONSMOUSEMAJOR, 0), VCHR); if (e = VOP_OPEN(&consmousevp, FREAD|FWRITE|FNOCTTY, u.u_cred)) { (void) VOP_CLOSE(mousevp, FREAD, 1, u.u_cred); return (e); } if ((fp = falloc()) == NULL) { (void) VOP_CLOSE(consmousevp, FREAD|FWRITE, 1, u.u_cred); (void) VOP_CLOSE(mousevp, FREAD, 1, u.u_cred); return (e = EINVAL); } fd = u.u_r.r_val1; /* XXX - get FD number */ fp->f_flag = FREAD; fp->f_type = DTYPE_VNODE; fp->f_data = (caddr_t) mousevp; fp->f_ops = &vnodefops; strioctl(consmousevp, I_PLINK, (caddr_t) &fd, FREAD); if ((e = u.u_error) != 0) { (void) VOP_CLOSE(consmousevp, FREAD|FWRITE, 1, u.u_cred); (void) VOP_CLOSE(mousevp, FREAD, 1, u.u_cred); return (e); } u.u_ofile[fd] = NULL; /* don't need this any more */ closef(fp); /* we're done with this, as well */ (void) VOP_CLOSE(consmousevp, FREAD|FWRITE, 1, u.u_cred); VN_RELE(consmousevp); return (e); } /* * Look for the [last] kbd/ms and default frame buffer. */ findcons(dev, p) struct dev_info *dev; struct consinfo *p; { struct dev_info *fbdevi; dev_t finddev(); static dev_t fbdevno = NODEV; extern char *prom_stdoutpath(); extern struct dev_info *path_to_devi(); /* options doesn't necessarily have a driver */ if (strcmp(dev->devi_name, "options") == 0) { p->options = dev; /* assume can't also be a frame buffer or zs */ return; } if (dev->devi_driver == NULL) return; if (strncmp(dev->devi_name, "zs", 2) == 0) { /* * production proms use "keyboard" property. * XXX pre-production proms use "flags". * XXX kernel must accept either, at least until FCS. */ if (getprop(dev->devi_nodeid, "keyboard", 0) || (getprop(dev->devi_nodeid, "flags", 0) & ZS_KBDMS)) { p->kmunit = dev->devi_unit; } /* assume that serial ports are not frame buffers */ return; } /* * It is possible for "path_to_devi" to return to us * a reference to a node that has no driver. If this * happens, finddev() will return NODEV, and things * will at least continue to function. */ if ((fbdevno == NODEV) && (prom_stdout_is_framebuffer())) if ((fbdevi = path_to_devi(prom_stdoutpath())) != 0) fbdevno = finddev('c', fbdevi); if (fbdevno != NODEV) p->fbdev = fbdevno; } #endif SAS /* convert dev_info pointer to device number */ dev_t finddev(type, info) int type; struct dev_info *info; { int majnum = -1; struct dev_ops *driver; int (*openfun)(); if (info == (struct dev_info *)0) return NODEV; driver = info->devi_driver; if (driver == (struct dev_ops *)0) return NODEV; openfun = driver->devo_open; if (openfun == (int (*)())0) return NODEV; if (type == 'b') { #ifdef notdef /* no one is using this at the moment */ struct bdevsw *swp = bdevsw; for (majnum = 0; majnum < nblkdev; majnum++, swp++) if (swp->d_open == openfun) return (makedev(majnum, info->devi_unit)); #endif } else { struct cdevsw *swp = cdevsw; for (majnum = 0; majnum < nchrdev; majnum++, swp++) if (swp->d_open == openfun) return (makedev(majnum, info->devi_unit)); } return (NODEV); } int keyclick; set_keyclick(dev) struct dev_info *dev; { addr_t key_click; int opt_nodeid; static char key_click_name[] = "keyboard-click?"; int size; if (dev == NULL) return; opt_nodeid = dev->devi_nodeid; size = getproplen(opt_nodeid, key_click_name); if (size <= 0) { printf("No \"%s\" property\n", key_click_name); return; } key_click = getlongprop(opt_nodeid, key_click_name); if (key_click) { if (strcmp(key_click, "true") == 0) keyclick = 1; kmem_free(key_click, (u_int)size); } } /* * Some things, like cputype, are contained in the idprom, but are * needed and obtained earlier; hence they are not set (again) here. */ idprom() { register u_char *cp, val = 0; register int i; struct idprom id; getidprom((char *)&id); cp = (u_char *)&id; for (i = 0; i < 16; i++) val ^= *cp++; if (val != 0) printf("Warning: NVRAM checksum error [%x]\n", val); if (id.id_format == 1) { (void) localetheraddr((struct ether_addr *)id.id_ether, (struct ether_addr *)NULL); } else printf("Invalid format code (%d) in NVRAM\n", id.id_format); } #ifdef NOPROM char *cpu_str = 0; char *mod_str = 0; extern int module_type; /* * We set the cpu type from the idprom machine ID. * XXX CPU name should be PROM property. */ setcputype() { struct idprom id; cpu = -1; /* not possible value for id.id_machine */ getidprom((char *)&id); if (id.id_format == 1) cpu = id.id_machine; else printf("Invalid format type (%d) in NVRAM\n", id.id_format); /* * This is just for pretty printing for now. */ switch (module_type) { case ROSS604: mod_str = "ross604"; break; case ROSS605: mod_str = "ross605"; break; case ROSS605_D: mod_str = "ross605d"; break; case VIKING: mod_str = "viking"; break; case VIKING_M: mod_str = "viking+mxcc"; break; case SSPARC: mod_str = "ssparc"; break; default: mod_str = "unknown"; break; } switch(cpu) { default: case -1: printf("machine type 0x%x in NVRAM\n", cpu); panic("No known machine types configured in!\n"); case CPU_SUN4M_690: cpu_str = "sun-4m/690"; break; case CPU_SUN4M_50: /* campus2 */ cpu_str = "sun-4m/50"; break; #ifdef SUN4M_35 case CPU_SUN4M_35: /* SPARCclassic & SPARCstation-LX */ cpu_str = "sun-4m/35"; break; #endif SUN4M_35 } } #else NOPROM setcputype() { struct idprom id; cpu = -1; /* not possible value for id.id_machine */ getidprom((char *)&id); if (id.id_format == 1) cpu = id.id_machine; else printf("Invalid format type (%d) in NVRAM\n", id.id_format); switch(cpu) { default: case -1: printf("machine type 0x%x in NVRAM\n", cpu); panic("No known machine types configured in!\n"); case CPU_SUN4M_690: case CPU_SUN4M_50: /* campus2 */ #if defined(SUN4M_35) case CPU_SUN4M_35: #endif break; } mach_info.sys_type = cpu; } #endif NOPROM machineid() { struct idprom id; register int x; getidprom((char *)&id); x = id.id_machine << 24; x += id.id_serial; return (x); } /* * Initialize pseudo-devices. * Reads count and init routine from table in ioconf.c * created by 'init blah' syntax on pseudo-device line in config file. * Calls init routine once for each unit configured */ extern struct pseudo_init { int ps_count; int (*ps_func)(); } pseudo_inits[]; pseudoconfig() { register struct pseudo_init *ps; int unit; for (ps = pseudo_inits; ps->ps_count > 0; ps++) for (unit = 0; unit < ps->ps_count; unit++) (*ps->ps_func)(unit); } extern unsigned sbus_numslots; extern unsigned sbus_basepage[]; extern unsigned sbus_slotsize[]; /* * a word about the INRANGE macro: * If addr < size, then the difference becomes a huge unsigned * number, which will compare true with the range if and only * if the size is big enough to wrap around across the end of * the number system. * * If addr > size, then we see if it is within the limit, which * is one *less* than the size -- this way, a size of zero becomes * a limit of 0xFFFFFFFF, corresponding to 4gig of space. * * Some simpler ways of doing this barf when the range ends at zero, * or when the size is 4gig, or when the range wraps across zero, or * when the range wraps across 0x80000000. Think carefully before you * change this macro. * * With all that work, maybe the macro should be used elsewhere! */ #define UNS(x) ((unsigned)(x)) #define INRANGE(addr, base, size) \ (UNS(UNS(addr) - UNS(base)) <= UNS(UNS(size)-1)) /* * INPAGE uses INRANGE to decide if an address falls inside * an area described by a page number and a size in bytes. * Note that page information above PA31 are lost, so space * comparisons (if any) must be done elsewhere. */ #define INPAGE(addr, page, bytes) \ INRANGE(addr, mmu_ptob(page), bytes) /* * INSBUSSLOT uses the INPAGE macro and the sbus layout information * from sbus_basepage and sbus_slotsize to decide if a physical * address is in a specific sbus slot. Since the physical address * is given only as a 32-bit number, the remainder of the PA must * be checked elsewhere. */ #define INSBUSSLOT(addr, slot) \ INPAGE(addr, sbus_basepage[slot], sbus_slotsize[slot]) /* * sbusslot returns the slot number for a given * physical address. Since only the low 32 bits * of the slot are given, that's all we check. */ int sbusslot(addr) unsigned addr; { int slot; for (slot=0; slotmc_driver; ++mc) (void)mbconfctrl(mc, mdr); /* * Now look for non-mass storage peripherals. */ for (md = mbdinit; mdr = md->md_driver; ++md) { if (md->md_alive || md->md_slave != -1) continue; (void)mbconfnonms(md, mdr); /* config non-mass storage device */ } } /* end of mbconfig */ #ifdef VME /* The routine to fill in the dev_info struct for VME devices * If configuring a controller the mb_device pointer will be * NULL, and vice-versa. * Common routine for mbconfctrl and mbconfnonms. * Using names passed from driver structures now, this will change. */ static void vme_fill_devinfo(mc, md, mdr, dev) register struct mb_ctlr *mc; register struct mb_device *md; struct mb_driver *mdr; struct dev_info *dev; { u_int space; /* * Allocate space for reg and intr structures * Dont have the luxury of calling getlongprop() */ dev->devi_reg = (struct dev_reg *)kmem_zalloc(sizeof(*dev->devi_reg)); dev->devi_intr = (struct dev_intr *)kmem_zalloc(sizeof(*dev->devi_intr)); /* %%% following may not always be true, but for now... */ dev->devi_nreg = 1; dev->devi_nintr = 1; /* Having a non-NULL devi_driver entry is necessary for boot */ dev->devi_driver = &pseudo_ops; if(mc) { /* configuring a controller */ dev->devi_name = mdr->mdr_cname; dev->devi_reg->reg_addr = mc->mc_addr; dev->devi_reg->reg_size = mdr->mdr_size; space = mc->mc_space; dev->devi_unit = mc->mc_ctlr; dev->devi_intr->int_pri = mc->mc_intpri; if (mc->mc_intr) /* %%% for IPI, idc doesnt intr */ dev->devi_intr->int_vec = mc->mc_intr->v_vec; } else if (md) { /* configuring a device */ dev->devi_name = mdr->mdr_dname; dev->devi_reg->reg_addr = md->md_addr; dev->devi_reg->reg_size = mdr->mdr_size; space = md->md_space; dev->devi_unit = md->md_unit; dev->devi_intr->int_pri = md->md_intpri; if (md->md_intr) dev->devi_intr->int_vec = md->md_intr->v_vec; } else { /* should never happen */ printf("vme_fill_devinfo: specified no controller/device\n"); } /* To get bustype from the space field */ switch (space & SP_BUSMASK) { case SP_VME16D16: case SP_VME24D16: case SP_VME32D16: dev->devi_reg->reg_bustype = 0xC; break; case SP_VME16D32: case SP_VME24D32: case SP_VME32D32: dev->devi_reg->reg_bustype = 0xD; break; default: /* This is where IPI falls */ dev->devi_reg->reg_bustype = 0x0; } } /* End of vme_fill_devinfo() */ #endif VME /* * Configure a controller */ mbconfctrl(mc, mdr) register struct mb_ctlr *mc; struct mb_driver *mdr; { struct mb_device *md; u_short *reg; int err = 0; int stat; u_short *doprobe(); struct dev_info *dev = NULL; if ((reg = doprobe((u_long)mc->mc_addr, (u_long)mc->mc_space, mdr, mdr->mdr_cname, mc->mc_ctlr, mc->mc_intpri, mc->mc_intr)) == 0) return (-1); #ifdef VME #if defined(SUN4M_35) if (no_vme) return (-1); #endif /* Check if already have controller listed in dev_info tree */ if (!vme_check_dupdev(vme_devinfo, mdr->mdr_cname, mc->mc_ctlr)) { /* * Do the work of adding device to dev_info tree */ dev = (struct dev_info *)kmem_zalloc(sizeof (*dev)); append_dev(vme_devinfo, dev); /* no nodeid to add, since not working with the PROM */ dev->devi_parent = vme_devinfo; vme_fill_devinfo(mc, (struct mb_device *) NULL, mdr, dev); /* no devops structure since not an sbus/fcode driver */ } if ((mdr->mdr_flags & (MDR_BIODMA | MDR_DMA)) != 0) adddma(mc->mc_intpri); mc->mc_alive = 1; mc->mc_mh = &mb_hd; mc->mc_addr = (caddr_t)reg; if (mdr->mdr_cinfo) mdr->mdr_cinfo[mc->mc_ctlr] = mc; for (md = mbdinit; md->md_driver; ++md) { /* * Dont check for alive here, so can run disks thru the * fill_devinfo code in mbconfdev */ if (md->md_driver != mdr || md->md_ctlr != mc->mc_ctlr && md->md_ctlr != '?') continue; stat = mbconfdev(md, mc, mdr, dev); /* configure the device */ if (err == 0) err = stat; } return (err); #else VME return (-1); #endif VME } /* end of mbconfctrl */ #ifdef VME add_ipi_drives(md, mdr) register struct mb_device *md; struct mb_driver *mdr; { struct dev_info *pd, *id; if (!found_vme) return (0); #ifdef lint mdr = mdr; #endif lint #ifdef IPI_DEBUG printf("Node %s unit %d at %s%d\n", mdr->mdr_dname, md->md_unit, mdr->mdr_cname, md->md_ctlr); #endif IPI_DEBUG for (pd = vme_devinfo->devi_slaves; pd != NULL; pd = pd->devi_next) { if (pn_identify(pd->devi_name)) { for (id = pd->devi_slaves; id != NULL; id = id->devi_next) { if ((ipi3sc_identify(id->devi_name)) && (md->md_ctlr == id->devi_unit)) (void) add_ipi_drive(id, md->md_unit); } } } return (0); } void add_ipi_drive(devi, unit) struct dev_info *devi; short unit; { register struct dev_info *dp, *ndp; #ifdef IPI_DEBUG printf("Parent Node %s%d\n", devi->devi_name, devi->devi_unit); #endif IPI_DEBUG dp = (struct dev_info *)kmem_zalloc(sizeof (struct dev_info)); dp->devi_name = "id"; dp->devi_unit = unit; dp->devi_nodeid = -1; dp->devi_driver = match_driver(dp->devi_name); dp->devi_parent = devi; if (devi->devi_slaves == NULL) { devi->devi_slaves = dp; } else { for (ndp = devi->devi_slaves; ndp != NULL; ndp = ndp->devi_next) if (ndp->devi_next == NULL) { ndp->devi_next = dp; break; } } } #endif VME /* * Configure a mass storage device. */ /* * Added "ctrl_dev" as argument passwd to this routine in order * to add this device as a child to it controller in dev_info tree. */ mbconfdev(md, mc, mdr, ctrl_dev) register struct mb_device *md; register struct mb_ctlr *mc; struct mb_driver *mdr; struct dev_info *ctrl_dev; { struct dev_info *dev_dev; if (((*mdr->mdr_slave)(md, mc->mc_addr)) && !(md->md_alive)) { md->md_alive = 1; md->md_ctlr = mc->mc_ctlr; md->md_hd = &mb_hd; md->md_addr = (caddr_t)mc->mc_addr; if (md->md_dk && dkn < DK_NDRIVE) md->md_dk = dkn++; else md->md_dk = -1; md->md_mc = mc; /* md_type comes from driver */ if (mdr->mdr_dinfo) mdr->mdr_dinfo[md->md_unit] = md; printf("%s%d at %s%d slave %d\n", mdr->mdr_dname, md->md_unit, mdr->mdr_cname, mc->mc_ctlr, md->md_slave); /* * If we were passed a ctrl_dev, add its children to the tree. */ if (ctrl_dev) { #ifdef VME /* * Check if already have device listed in dev_info tree */ if (!vme_check_dupdev(ctrl_dev, mdr->mdr_dname, md->md_unit)) { /* * Add the device to dev_info tree as a child of its controller. */ dev_dev = (struct dev_info *)kmem_zalloc(sizeof (*dev_dev)); append_dev(ctrl_dev, dev_dev); /* no nodeid */ dev_dev->devi_parent = ctrl_dev; vme_fill_devinfo(( struct mb_ctlr *) NULL, md, mdr, dev_dev); /* no devops structure */ } #endif VME } if (mdr->mdr_attach) (*mdr->mdr_attach)(md); return (0); } return (-1); } /* end of mbconfdev */ /* * Configure a non-mass storage device */ mbconfnonms(md, mdr) register struct mb_device *md; register struct mb_driver *mdr; { u_short *reg; struct dev_info *dev; if ((reg = doprobe((u_long)md->md_addr, (u_long)md->md_space, mdr, mdr->mdr_dname, md->md_unit, md->md_intpri, md->md_intr)) == 0) return (-1); #ifdef VME #if defined(SUN4M_35) if (no_vme) return (-1); #endif /* Check if already have controller listed in dev_info tree */ if (vme_check_dupdev(vme_devinfo, mdr->mdr_dname, md->md_unit) == 0) { /* * Do the work of adding device to VME dev_info tree */ dev = (struct dev_info *)kmem_zalloc(sizeof (*dev)); append_dev(vme_devinfo, dev); /* no nodeid to add, since not working with the PROM */ dev->devi_parent = vme_devinfo; vme_fill_devinfo(( struct mb_ctlr *) NULL, md, mdr, dev); /* no devops structure since not an sbus/fcode driver */ } if ((mdr->mdr_flags & (MDR_BIODMA | MDR_DMA)) != 0) adddma(md->md_intpri); md->md_hd = &mb_hd; md->md_alive = 1; md->md_addr = (caddr_t)reg; md->md_dk = -1; /* md_type comes from driver */ if (mdr->mdr_dinfo) mdr->mdr_dinfo[md->md_unit] = md; if (mdr->mdr_attach) (*mdr->mdr_attach)(md); return (0); #else VME return (-1); #endif VME } /* end of mbconfnonms */ /* * Probe for a device or controller at the specified addr. * The space argument give the page type and cpu type for the device. * * This code is now only for VME devices. * *****We can no longer probe for SBus devices.***** */ u_short *doprobe(addr, space, mdr, dname, unit, br, vp) u_long addr; u_long space; struct mb_driver *mdr; char *dname; int unit; int br; register struct vec *vp; { register u_short *reg = NULL; char *name; unsigned base = 0; int i; int is_ipi = 1; #ifdef VME #if defined(SUN4M_35) if (no_vme) return (0); #endif /* * %%% this routine "knows" about the space and base * addresses of all the VME mapping areas. Ugh. */ switch (space & SP_BUSMASK) { case SP_VME16D16: name = "vme16d16"; space = 0xC; base = 0xFFFF0000; break; case SP_VME16D32: name = "vme16d32"; space = 0xD; base = 0xFFFF0000; break; case SP_VME24D16: name = "vme24d16"; space = 0xC; base = 0xFF000000; break; case SP_VME24D32: name = "vme24d32"; space = 0xD; base = 0xFF000000; break; case SP_VME32D16: name = "vme32d16"; space = 0xC; break; case SP_VME32D32: name = "vme32d32"; space = 0xD; break; case SP_IPI: name = "ipi"; reg = (u_short *)addr; /* IPI device address */ br = 0; /* IPI interrupts thru host adaptor */ break; default: return (0); } if (reg == NULL) { reg = (u_short *)map_regs((addr_t)(base + addr), (u_int) mdr->mdr_size, (int) space); is_ipi = 0; } i = (*mdr->mdr_probe)(reg, unit); if (i == 0) { if (!is_ipi) { unmap_regs((addr_t) reg, (u_int) mdr->mdr_size); } return (0); } printf("%s%d at %s 0x%x ", dname, unit, name, addr); if (br < 0 || br >= 7) { printf("bad priority (%d)\n", br); unmap_regs((addr_t) reg, (u_int) mdr->mdr_size); return (0); } /* * If br is 0, then no priority was specified in the * config file and the device cannot use interrupts. */ if (br != 0) { /* * now set up VME vectored interrupts if conditions are right */ if (vp != (struct vec *)0) { for (; vp->v_func; vp++) { void vme_vecinstall(); printf("vec 0x%x ", vp->v_vec); vme_vecinstall(vp); } } } printf("\n"); return (reg); #else VME return (0); #endif VME } #ifdef VME void vme_vecinstall(vp) struct vec *vp; { int vec_num; if (vp->v_vec < VEC_MIN || vp->v_vec > VEC_MAX) panic("vme_vecinstall: bad vector"); vec_num = vp->v_vec - VEC_MIN; if (vme_vector[vec_num].func != 0) panic("vme_vecinstall: vector in use"); vme_vector[vec_num].func = vp->v_func; vme_vector[vec_num].arg = vp->v_vptr; } /* end of vme_vecinstall */ #endif VME