2139 lines
51 KiB
C
2139 lines
51 KiB
C
#ident "@(#)autoconf.c 1.1 94/10/31 SMI"
|
|
|
|
/*
|
|
* Copyright (c) 1987-1989 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 <sys/param.h>
|
|
#define OPENPROMS
|
|
#include <sys/user.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/vfs_stat.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/map.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/dk.h>
|
|
#include <sys/vm.h>
|
|
#include <sys/file.h>
|
|
#include <sys/termios.h>
|
|
#include <sys/termio.h>
|
|
#include <sys/ttold.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/mman.h>
|
|
#undef NFS
|
|
#include <sys/mount.h>
|
|
#include <sys/bootconf.h>
|
|
#include <specfs/snode.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <vm/seg.h>
|
|
|
|
#include <machine/pte.h>
|
|
#include <machine/mmu.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/scb.h>
|
|
#include <machine/trap.h>
|
|
#include <machine/psl.h>
|
|
#include <machine/seg_kmem.h>
|
|
#include <sun/autoconf.h>
|
|
#include <sun/consdev.h>
|
|
#include <machine/devr.h>
|
|
#include <sundev/mbvar.h>
|
|
#include <sundev/kbio.h>
|
|
#include <sundev/zsvar.h>
|
|
#include <mon/idprom.h>
|
|
#include <sys/debug.h>
|
|
#include <sys/reboot.h>
|
|
|
|
|
|
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
|
|
*/
|
|
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;
|
|
|
|
#ifndef SAS
|
|
/*
|
|
* 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, 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.
|
|
*/
|
|
static void
|
|
fill_devinfo(dev_info)
|
|
struct dev_info *dev_info;
|
|
{
|
|
int nodeid;
|
|
int (*decode_func)();
|
|
static int curzs = 0;
|
|
|
|
nodeid = dev_info->devi_nodeid;
|
|
dev_info->devi_name = (char *)getlongprop(nodeid, "name");
|
|
dev_info->devi_nintr = getproplen(nodeid, "intr") /
|
|
sizeof (struct dev_intr);
|
|
/*
|
|
* We try to match stdin/stdout while attaching the first "zs".
|
|
* (Try a more generic approach like -1.)
|
|
*/
|
|
|
|
if (strcmp("zs", dev_info->devi_name) == 0)
|
|
dev_info->devi_unit = curzs++;
|
|
|
|
if (dev_info->devi_nintr > 0)
|
|
dev_info->devi_intr =
|
|
(struct dev_intr *)getlongprop(nodeid, "intr");
|
|
/*
|
|
* V.2 Open Boot proms report "reg" addresses for children of
|
|
* hierarchical devices in the address space of the parent
|
|
* device. If necessary, call a bus-specific decoding routine
|
|
* to unpack the reg struct.
|
|
*/
|
|
if ((prom_getversion() > 0) &&
|
|
(decode_func = path_getdecodefunc(dev_info->devi_parent))) {
|
|
dev_info->devi_nreg = decode_func(dev_info, nodeid);
|
|
return;
|
|
}
|
|
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");
|
|
};
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static 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);
|
|
}
|
|
|
|
/*
|
|
* This routine appends the second argument onto the slave list for the
|
|
* first argument. It always appends it to the end of the list.
|
|
*/
|
|
static 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;
|
|
}
|
|
|
|
#else SAS
|
|
/*
|
|
* STEVE- you'll have to fix this...
|
|
*/
|
|
|
|
struct dev_info sbus_info;
|
|
struct dev_info sas_info;
|
|
|
|
struct dev_info sas_info = {
|
|
NULL, /* devi_parent; */
|
|
NULL, /* devi_next; */
|
|
&sbus_info, /* devi_slaves; */
|
|
"campus-sas", /* devi_name; */
|
|
0, /* devi_nreg; */
|
|
NULL, /* devi_reg; */
|
|
0, /* devi_nintr; */
|
|
NULL, /* devi_intr; */
|
|
NULL, /* devi_driver; */
|
|
NULL, /* devi_data; */
|
|
0, /* devi_nodeid; */
|
|
0, /* devi_unit; */
|
|
};
|
|
|
|
struct dev_info sbus_info = {
|
|
&sas_info, /* devi_parent; */
|
|
NULL, /* devi_next; */
|
|
NULL, /* devi_slaves; */
|
|
"sbus", /* devi_name; */
|
|
0, /* devi_nreg; */
|
|
NULL, /* devi_reg; */
|
|
0, /* devi_nintr; */
|
|
NULL, /* devi_intr; */
|
|
NULL, /* devi_driver; */
|
|
NULL, /* devi_data; */
|
|
1, /* devi_nodeid; */
|
|
0, /* devi_unit; */
|
|
};
|
|
|
|
|
|
#endif SAS
|
|
|
|
/*
|
|
* 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 configure computes SPLMB based
|
|
* on it.
|
|
*
|
|
* XXX SPLMB is for backward compatibility only.
|
|
* XXX Drivers should call splvm() instead of splr(pritospl(SPLMB)).
|
|
*/
|
|
int SPLMB = 4; /* reasonable default */
|
|
|
|
/*
|
|
* We use splvmval to implement splvm().
|
|
*/
|
|
int splvm_val = pritospl(4); /* reasonable default */
|
|
|
|
/*
|
|
* 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 the I/O 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.
|
|
*/
|
|
mbconfig()
|
|
{
|
|
|
|
#ifndef SAS
|
|
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));
|
|
/*
|
|
* XXX - This call ALSO patches the DMA+ bug in Campus-B and
|
|
* Phoenix. The prom uncaches the traptable page as a side-effect
|
|
* of devr_next(0), so this *must* be executed early on.
|
|
*/
|
|
top_devinfo->devi_nodeid = (int)prom_nextnode((dnode_t)0);
|
|
fill_devinfo(top_devinfo);
|
|
printf("cpu = %s\n", top_devinfo->devi_name);
|
|
attach_devs(top_devinfo);
|
|
#endif !SAS
|
|
}
|
|
|
|
#ifndef SAS
|
|
static int nsbus = 0; /* number of sbus's */
|
|
static int cursbus = 0; /* current unit number for attach */
|
|
int sbus_identify();
|
|
int sbus_attach();
|
|
struct dev_ops sbus_ops = {
|
|
1,
|
|
sbus_identify,
|
|
sbus_attach,
|
|
};
|
|
|
|
int
|
|
sbus_identify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "sbus") == 0) {
|
|
nsbus++;
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
sbus_attach(devi)
|
|
struct dev_info *devi;
|
|
{
|
|
|
|
/*
|
|
* We increment the unit number for each sbus we see.
|
|
*/
|
|
devi->devi_unit = cursbus++;
|
|
report_dev(devi);
|
|
attach_devs(devi);
|
|
return (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 dnode_t curnode;
|
|
dnode_t prom_nextnode(), prom_childnode();
|
|
|
|
curnode = prom_childnode((dnode_t)(dev_info->devi_nodeid));
|
|
if (curnode == 0)
|
|
return;
|
|
for (; curnode != 0;
|
|
curnode = prom_nextnode(curnode)) {
|
|
/*
|
|
* If it's already on the list, skip it.
|
|
*/
|
|
if (check_dupdev(dev_info, (int)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 = (int)curnode;
|
|
dev->devi_parent = dev_info;
|
|
fill_devinfo(dev);
|
|
dev->devi_driver = match_driver(dev->devi_name);
|
|
/*
|
|
* 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) {
|
|
#ifdef DAVE_DOESNT_WANT_THIS_ANYMORE
|
|
printf("Warning: kernel doesn't support device '%s'\n",
|
|
dev->devi_name);
|
|
#endif
|
|
#ifndef VDDRV
|
|
if (remove_dev(dev_info, dev))
|
|
panic("attach_devs remove_dev 1");
|
|
(void) kmem_free((caddr_t) dev, sizeof (*dev));
|
|
#endif VDDRV
|
|
continue;
|
|
}
|
|
if (dev->devi_driver->devo_attach(dev) < 0) {
|
|
#ifdef VDDRV
|
|
dev->devi_driver = NULL;
|
|
#else VDDRV
|
|
if (remove_dev(dev_info, dev))
|
|
panic("attach_devs remove_dev 2");
|
|
(void) kmem_free((caddr_t) dev, sizeof (*dev));
|
|
#endif VDDRV
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif !SAS
|
|
|
|
#ifdef VDDRV
|
|
struct add_drv_info {
|
|
struct dev_ops *adi_ops;
|
|
int adi_count;
|
|
};
|
|
|
|
/*
|
|
* 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;
|
|
void add_drv_layer();
|
|
|
|
adi.adi_ops = dev_ops;
|
|
adi.adi_count = 0;
|
|
|
|
walk_slaves(top_devinfo,
|
|
(int (*)()) add_drv_layer, (caddr_t) &adi);
|
|
return (adi.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;
|
|
};
|
|
|
|
/*
|
|
* 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;
|
|
{
|
|
struct new_devlist_head new_devlist_head;
|
|
register struct new_devlist *ndp;
|
|
void add_a_device();
|
|
int attached = 0;
|
|
|
|
new_devlist_head.ndh_head = new_devlist_head.ndh_tail = NULL;
|
|
walk_layer(dev,
|
|
(int (*)()) add_a_device, (caddr_t) &new_devlist_head);
|
|
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);
|
|
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;
|
|
|
|
/* Now see if any slaves of this layer need attaching */
|
|
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);
|
|
}
|
|
}
|
|
|
|
#ifndef SAS
|
|
/*
|
|
* Given a physical address, a bus type, and a size, return a virtual
|
|
* address that maps the registers.
|
|
* Returns NULL on any 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;
|
|
|
|
if (space == SPO_VIRTUAL)
|
|
reg = addr;
|
|
else
|
|
pageval = MAKE_PGT(space) | btop(addr);
|
|
|
|
if (reg == NULL) {
|
|
int offset = (int)addr & PGOFSET;
|
|
long a = 0;
|
|
int extent;
|
|
|
|
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);
|
|
}
|
|
|
|
#ifdef notused
|
|
/*
|
|
* 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.
|
|
*/
|
|
decode_address(space, addr)
|
|
int space, addr;
|
|
{
|
|
char *name;
|
|
|
|
/* Here we encode the knowledge that the SBUS is part of OBIO space */
|
|
if (space == OBIO && addr >= SBUS_BASE) {
|
|
printf(" SBus slot %d 0x%x", (addr - SBUS_BASE)/SBUS_SIZE,
|
|
(addr % SBUS_SIZE) & (SBUS_SIZE-1));
|
|
return;
|
|
}
|
|
|
|
/* Here we enumerate the set of physical address types */
|
|
switch (space) {
|
|
case SPO_VIRTUAL: name = "virtual"; break;
|
|
case OBMEM: name = "obmem"; break;
|
|
case OBIO: name = "obio"; break;
|
|
default: name = "unknown"; break;
|
|
}
|
|
|
|
printf(" %s 0x%x", name, addr);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Say that a device is here
|
|
*/
|
|
|
|
void
|
|
report_dev(dev)
|
|
register struct dev_info *dev;
|
|
{
|
|
register struct dev_reg *rp;
|
|
register struct dev_intr *ip;
|
|
int i;
|
|
|
|
printf("%s%d", dev->devi_name, dev->devi_unit);
|
|
for (i = 0, rp = dev->devi_reg; i < dev->devi_nreg; i++, rp++) {
|
|
if (i == 0)
|
|
printf(" at");
|
|
else
|
|
printf(" and");
|
|
decode_address(rp->reg_bustype, (int) rp->reg_addr);
|
|
}
|
|
for (i = 0, ip = dev->devi_intr; i < dev->devi_nintr; i++, ip++) {
|
|
if (i == 0)
|
|
printf(" ");
|
|
else
|
|
printf(", ");
|
|
printf("pri %d", ip->int_pri);
|
|
if (ip->int_vec)
|
|
printf(" vec 0x%x", ip->int_vec);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
#endif !SAS
|
|
|
|
/*
|
|
* 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, (caddr_t)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);
|
|
(void)prom_getprop((dnode_t)id, (caddr_t)name, (caddr_t)value);
|
|
return (value);
|
|
}
|
|
|
|
/*
|
|
* Spurious interrupt messages
|
|
*/
|
|
|
|
#define SPURIOUS -1 /* recognized in locore.s */
|
|
|
|
static
|
|
not_serviced(counter, level)
|
|
int *counter, level;
|
|
{
|
|
if ((*counter)++ % 100 == 1)
|
|
printf("iobus level %d interrupt not serviced\n", level);
|
|
return (SPURIOUS);
|
|
}
|
|
|
|
int level1_spurious;
|
|
not_serviced1() { return not_serviced(&level1_spurious, 1); }
|
|
|
|
int level2_spurious;
|
|
not_serviced2() { return not_serviced(&level2_spurious, 2); }
|
|
|
|
int level3_spurious;
|
|
not_serviced3() { return not_serviced(&level3_spurious, 3); }
|
|
|
|
int level5_spurious;
|
|
not_serviced5() { return not_serviced(&level5_spurious, 5); }
|
|
|
|
int level6_spurious;
|
|
not_serviced6() { return not_serviced(&level6_spurious, 6); }
|
|
|
|
int level7_spurious;
|
|
not_serviced7() { return not_serviced(&level7_spurious, 7); }
|
|
|
|
int level8_spurious;
|
|
not_serviced8() { return not_serviced(&level8_spurious, 8); }
|
|
|
|
int level9_spurious;
|
|
not_serviced9() { return not_serviced(&level9_spurious, 9); }
|
|
|
|
int level11_spurious;
|
|
not_serviced11() { return not_serviced(&level11_spurious, 11); }
|
|
|
|
int level13_spurious;
|
|
not_serviced13() { return not_serviced(&level13_spurious, 13); }
|
|
|
|
|
|
typedef int (*func)();
|
|
extern softlevel1();
|
|
|
|
/*
|
|
* 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).
|
|
*/
|
|
struct autovec level1[NVECT] = {{softlevel1}, {not_serviced1}};
|
|
struct autovec level2[NVECT] = {{not_serviced2}};
|
|
struct autovec level3[NVECT] = {{not_serviced3}};
|
|
struct autovec level4[NVECT] = {{NULL}}; /* special - see locore */
|
|
struct autovec level5[NVECT] = {{not_serviced5}};
|
|
struct autovec level6[NVECT] = {{not_serviced6}};
|
|
struct autovec level7[NVECT] = {{not_serviced7}};
|
|
struct autovec level8[NVECT] = {{not_serviced8}};
|
|
struct autovec level9[NVECT] = {{not_serviced9}};
|
|
struct autovec level11[NVECT] = {{not_serviced11}};
|
|
struct autovec level13[NVECT] = {{not_serviced13}};
|
|
|
|
/*
|
|
* 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;
|
|
{
|
|
#ifdef oldstuff
|
|
register int k;
|
|
register int index = 0;
|
|
register char **nt;
|
|
char devunit;
|
|
int namelen;
|
|
#endif oldstuff
|
|
|
|
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++;
|
|
|
|
#ifdef oldstuff
|
|
/* FIXME - we don't use av_nametab anymore */
|
|
/*
|
|
* Point levelp->name at the dev_info name (need the devinfo
|
|
* name! or is "name" it?), and increment levelp->device_count
|
|
*/
|
|
devunit = (char)((int)'0' + unit);
|
|
namelen = strlen(name);
|
|
if (levelp->av_names != 0) {
|
|
av_nametab[levelp->av_names - 1][namelen] = '?';
|
|
return;
|
|
}
|
|
for (nt = av_nametab, k = 0; nt < av_end; nt++, k++) {
|
|
if (strncmp(name, *nt, namelen) == 0) {
|
|
index = k + 1; /* 0 ==> uninitialized */
|
|
if ((*nt)[namelen] == devunit)
|
|
break;
|
|
}
|
|
}
|
|
if (index != 0) {
|
|
levelp->av_names = index;
|
|
av_nametab[index - 1][namelen] = devunit;
|
|
}
|
|
#endif oldstuff
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
addintr(lvl, xxintr, name, unit)
|
|
func xxintr;
|
|
char *name;
|
|
int unit;
|
|
{
|
|
register func f;
|
|
register struct autovec *levelp;
|
|
register int i;
|
|
|
|
switch (lvl) {
|
|
case 1: levelp = level1; break;
|
|
case 2: levelp = level2; break;
|
|
case 3: levelp = level3; break;
|
|
case 4: levelp = level4; break;
|
|
case 5: levelp = level5; break;
|
|
case 6: levelp = level6; break;
|
|
case 7: levelp = level7; break;
|
|
case 8: levelp = level8; break;
|
|
case 9: levelp = level9; break;
|
|
case 11: levelp = level11; break;
|
|
case 13: levelp = level13; break;
|
|
default:
|
|
printf(
|
|
"addintr: cannot set up polling interrupt on processor level %d\n", lvl);
|
|
panic("addintr");
|
|
/*NOTREACHED*/
|
|
}
|
|
if ((f = xxintr) == NULL)
|
|
return;
|
|
|
|
/* 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) /* end of list found */
|
|
break;
|
|
if (levelp->av_vector == f) { /* already in list */
|
|
addioname(levelp, name, unit);
|
|
return;
|
|
}
|
|
levelp++;
|
|
}
|
|
if (i >= NVECT)
|
|
panic("addintr: too many devices");
|
|
/* if nobody there (like on level4) fake a dummy */
|
|
if (i == 0)
|
|
levelp++;
|
|
levelp[0] = levelp[-1]; /* move not_serviced to end */
|
|
/* must clear the new entry */
|
|
bzero((caddr_t) &levelp[-1], sizeof (*levelp));
|
|
levelp[-1].av_vector = f; /* add f to list */
|
|
addioname(levelp - 1, name, unit);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* 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;
|
|
int offset;
|
|
extern sys_trap();
|
|
extern int nwindows; /* number of register windows */
|
|
|
|
/* Find the appropriate autovector table */
|
|
switch (lvl) {
|
|
case 1: levelp = level1; break;
|
|
case 2: levelp = level2; break;
|
|
case 3: levelp = level3; break;
|
|
case 4: levelp = level4; break;
|
|
case 5: levelp = level5; break;
|
|
case 6: levelp = level6; break;
|
|
case 7: levelp = level7; break;
|
|
case 8: levelp = level8; break;
|
|
case 9: levelp = level9; break;
|
|
case 11: levelp = level11; break;
|
|
case 13: levelp = level13; break;
|
|
case 10: case 12: case 14: case 15: levelp = NULL; break;
|
|
default:
|
|
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;
|
|
|
|
/* Construct a load instruction */
|
|
vec.instr[1] = MOVL4 | T_INTERRUPT | lvl;
|
|
|
|
/* Branch to 'sys_trap' */
|
|
vec.instr[2] = BRANCH | (BRANCH_MASK & (((int)sys_trap -
|
|
(int)&scb.interrupts[lvl - 1].instr[2]) >> 2));
|
|
|
|
vec.instr[3] = MOVL6 | (nwindows - 1);
|
|
|
|
} else {
|
|
vec.instr[0] = MOVPSRL0;
|
|
|
|
/* Construct a branch instruction with the given trap address */
|
|
offset = (((int)xxintr -
|
|
(int)&scb.interrupts[lvl - 1].instr[1]) >> 2);
|
|
|
|
/* Check that branch displacement is within range */
|
|
if (((offset & ~BRANCH_RANGE) == 0) ||
|
|
((offset & ~BRANCH_RANGE) == ~BRANCH_RANGE)) {
|
|
vec.instr[1] = BRANCH | (offset & BRANCH_MASK);
|
|
vec.instr[2] = MOVL6 | (nwindows - 1);
|
|
} else {
|
|
/* Branch is too far, so use a jump */
|
|
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(level);
|
|
if (spl > splvm_val)
|
|
splvm_val = spl;
|
|
SPLMB = spltopri(splvm_val); /* XXX this may round, */
|
|
splvm_val = pritospl(SPLMB); /* XXX so convert back */
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
switch (lvl) {
|
|
|
|
case 1: levelp = level1; break;
|
|
case 2: levelp = level2; break;
|
|
case 3: levelp = level3; break;
|
|
case 4: levelp = level4; break;
|
|
case 5: levelp = level5; break;
|
|
case 6: levelp = level6; break;
|
|
case 7: levelp = level7; break;
|
|
case 8: levelp = level8; break;
|
|
case 9: levelp = level9; break;
|
|
case 11: levelp = level11; break;
|
|
case 13: levelp = level13; break;
|
|
default:
|
|
printf(
|
|
"remintr: cannot remove polling interrupt on processor level %d\n", lvl);
|
|
panic("remintr");
|
|
}
|
|
if ((f = xxintr) == NULL)
|
|
return (0);
|
|
|
|
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 swap file %s\n",
|
|
error, swp->bo_name);
|
|
/*
|
|
* Let the release of the vnode above cause the call
|
|
* of the close routine for the device.
|
|
*/
|
|
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");
|
|
}
|
|
/* swp->bo_vp = makespecvp(makedev(1, 1), VBLK); */
|
|
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;
|
|
#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;
|
|
|
|
#ifdef XPRINTF
|
|
XPRINTF("consconfig: rconsdev zs minor device number <%d>\n", zsminor);
|
|
#endif XPRINTF
|
|
|
|
rconsdev = makedev(ZSMAJOR, zsminor);
|
|
|
|
#else SAS
|
|
rconsdev = makedev(SIMCMAJOR, 0);
|
|
#endif SAS
|
|
|
|
namedone:
|
|
|
|
if (rconsdev) {
|
|
/*
|
|
* Console is a CPU serial port.
|
|
*/
|
|
|
|
#ifdef XPRINTF
|
|
XPRINTF("Console is a serial device\n");
|
|
#endif XPRINTF
|
|
|
|
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()) {
|
|
#ifdef XPRINTF
|
|
XPRINTF("consconfig <kbd>: Using physical keyboard\n");
|
|
#endif XPRINTF
|
|
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;
|
|
|
|
#ifdef XPRINTF
|
|
XPRINTF("consconfig: remote kbddev zs minor dev <%d>\n",
|
|
zsminor);
|
|
#endif XPRINTF
|
|
|
|
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;
|
|
{
|
|
static int fbid;
|
|
dev_t finddev();
|
|
|
|
/* 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;
|
|
}
|
|
|
|
if (fbid == 0)
|
|
fbid = getprop(top_devinfo->devi_nodeid, "fb", -1);
|
|
|
|
if (fbid == dev->devi_nodeid)
|
|
p->fbdev = finddev('c', dev);
|
|
|
|
}
|
|
/*#endif SAS*/
|
|
|
|
/* convert dev_info pointer to device number */
|
|
dev_t
|
|
finddev(type, info)
|
|
int type;
|
|
struct dev_info *info;
|
|
{
|
|
int majnum = -1;
|
|
int (*openfun)();
|
|
|
|
openfun = info->devi_driver->devo_open;
|
|
|
|
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)
|
|
break;
|
|
#endif
|
|
} else {
|
|
struct cdevsw *swp = cdevsw;
|
|
|
|
for (majnum = 0; majnum < nchrdev; majnum++, swp++)
|
|
if (swp->d_open == openfun)
|
|
break;
|
|
}
|
|
|
|
if (majnum < 0)
|
|
return (NODEV);
|
|
|
|
return (makedev(majnum, info->devi_unit));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
#endif SAS
|
|
/*
|
|
* 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\n");
|
|
if (id.id_format == 1) {
|
|
(void) localetheraddr((struct ether_addr *)id.id_ether,
|
|
(struct ether_addr *)NULL);
|
|
} else
|
|
printf("Invalid format code in NVRAM\n");
|
|
}
|
|
|
|
/*
|
|
* We set the cpu type from the idprom machine ID.
|
|
* XXX CPU name should be PROM property.
|
|
*/
|
|
setcputype()
|
|
{
|
|
struct idprom id;
|
|
|
|
cpu = -1;
|
|
|
|
getidprom((char *)&id);
|
|
if (id.id_format == 1) {
|
|
cpu = id.id_machine;
|
|
if ((cpu & CPU_ARCH) != SUN4C_ARCH) {
|
|
printf("Unknown machine type 0x%x in NVRAM\n", cpu);
|
|
cpu = -1;
|
|
}
|
|
} else {
|
|
printf("Invalid format type in NVRAM\n");
|
|
}
|
|
|
|
if (cpu == -1) {
|
|
printf("Machine type set to Sun-4/60\n");
|
|
cpu = CPU_SUN4C_60;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#ifndef SAS
|
|
/*
|
|
* Return slot number if given physical address corresponds to
|
|
* slave-only SBus slot, e.g. 4/60 slot 3; return -1 if OK.
|
|
*/
|
|
slaveslot(addr)
|
|
addr_t addr;
|
|
{
|
|
int slot;
|
|
|
|
slot = (((int) addr) - SBUS_BASE) / SBUS_SIZE;
|
|
return ((getprop(0, "slave-only", 8) & (1 << slot)) ? slot : -1);
|
|
}
|
|
#endif !SAS
|