Files
Arquivotheca.SunOS-4.1.4/sys/sun4c/autoconf.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

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