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

620 lines
13 KiB
C

#ident "@(#)openprom_util.c 1.1 94/10/31 SMI"
/*LINTLIBRARY*/
/*
* Copyright (c) 1990 Sun Microsystems, Inc.
*/
/*
* Routines for OBP boot support.
*/
#define OPENPROMS
#include <mon/obpdefs.h>
#include <sun/openprom.h>
#include <sun/autoconf.h>
#include <scsi/scsi.h>
#ifndef sun4c
/*
* None of the decoding stuff is used by sun4m, since they
* have "range" properties available. c.f. autoconf.c:apply_range()
* Also, for most devices, a generic encoding function is used
* that pulls out the reg property from the PROM and uses that as the
* qualifier.
*/
static int units_are_hex();
#endif ! sun4c
#define CLRBUF(a, l) \
{ \
register int c = l; \
register char *p = (char *)a; \
while (c-- > 0) \
*p++ = 0; \
}
/*
* Define DPRINTF and set (both) debug flags to non-zero for debugging output.
*
*/
#define DPRINTF 1
#ifdef DPRINTF
static int memlistdebug = 0;
#define dprintf if (memlistdebug) printf
#endif DPRINTF
struct memlist *
getmemlist(name, prop)
char *name, *prop;
{
int nodeid;
int i, j, k, chunks;
u_int propsize;
struct dev_reg *rp;
struct memlist *pmem;
static caddr_t bufp = (caddr_t)0;
static u_int left;
extern caddr_t prom_alloc();
extern dnode_t prom_nextnode();
if (bufp == (caddr_t)0) {
bufp = prom_alloc((caddr_t)0, (u_int)PAGESIZE); /* XXX */
left = PAGESIZE;
}
/*
* First find the right node.
*/
nodeid = searchpromtree((int)prom_nextnode((dnode_t)0), name, bufp);
if (nodeid == 0)
return ((struct memlist *)0);
#ifdef DPRINTF
dprintf("Found %s:%s at node %x\n", name, prop, nodeid);
#endif DPRINTF
/*
* Allocate memlist elements, one per dev_reg struct.
*/
propsize = (u_int)getproplen(nodeid, prop);
chunks = propsize / sizeof (struct dev_reg);
if (chunks == 0)
return ((struct memlist *)0);
if (left < (chunks * sizeof (struct memlist))) {
panic("memlists too big");
}
pmem = (struct memlist *)bufp;
CLRBUF(pmem, (u_int) chunks * sizeof (struct memlist));
left -= (u_int) chunks * sizeof (struct memlist);
bufp += (u_int) chunks * sizeof (struct memlist);
if (left < propsize) {
panic("memlists too big");
}
rp = (struct dev_reg *)bufp;
CLRBUF(rp, propsize);
left -= propsize;
bufp += propsize;
/*
* Fill in memlist chunks.
*/
(void)prom_getprop((dnode_t)nodeid, prop, (caddr_t) rp);
for (i = 0; i < chunks; ++i) {
#ifdef DPRINTF
dprintf("Chunk %d: addr %x bustype %x size %x\n",
i, rp[i].reg_addr, rp[i].reg_bustype, rp[i].reg_size);
#endif DPRINTF
if (rp[i].reg_bustype != OBMEM)
continue;
/*
* Insert the new element into the array of list nodes
* so that the array remains sorted in ascending order.
*/
/* Find the first element with a larger address */
for (j = 0; j < i; j++)
if (pmem[j].address > (u_int)rp[i].reg_addr)
break;
/* Copy following elements up to make room for the new one */
for (k = i; k > j; --k)
pmem[k] = pmem[k-1];
/* Insert the new element */
pmem[j].address = (u_int)rp[i].reg_addr;
pmem[j].size = rp[i].reg_size;
}
for (i = 1; i < chunks; ++i) {
if (pmem[i].address)
pmem[i-1].next = &pmem[i];
}
return (pmem);
}
searchpromtree(node, name, buf)
int node;
char *name;
char *buf;
{
int id;
extern dnode_t prom_nextnode(), prom_childnode();
*buf = '\0';
(void)prom_getprop((dnode_t)node, "name", buf);
if (strcmp(buf, name) == 0) {
return (node);
}
id = (int)prom_nextnode((dnode_t)node);
if (id && (id = searchpromtree(id, name, buf)))
return (id);
id = prom_childnode((dnode_t)node);
if (id && (id = searchpromtree(id, name, buf)))
return (id);
return (0);
}
#ifdef DPRINTF
static int path_debug = 0;
#undef dprintf
#define dprintf if (path_debug) printf
#endif DPRINTF
int (*
path_getdecodefunc(dip))()
struct dev_info *dip;
{
struct dev_path_ops *dpop;
for (dpop = dev_path_opstab; dip && dpop->devp_name; ++dpop) {
if (strcmp(dip->devi_name, dpop->devp_name) == 0)
return (dpop->devp_decode_regprop);
}
return ((int (*)())0);
}
char *(*
path_getencodefunc(dip))()
struct dev_info *dip;
{
struct dev_path_ops *dpop;
#ifdef DPRINTF
dprintf("path_getencodefunc: for device <%s>\n", dip->devi_name);
#endif DPRINTF
for (dpop = dev_path_opstab; dip && dpop->devp_name; ++dpop) {
if (strcmp(dip->devi_name, dpop->devp_name) == 0)
return (dpop->devp_encode_reg);
}
return ((char *(*)())0);
}
int (*
path_getmatchfunc(dip))()
struct dev_info *dip;
{
struct dev_path_ops *dpop;
for (dpop = dev_path_opstab; dip && dpop->devp_name; ++dpop) {
if (strcmp(dip->devi_name, dpop->devp_name) == 0)
return (dpop->devp_match_addr);
}
return ((int (*)())0);
}
static char *
path_gettoken(from, to)
char *from, *to;
{
while (*from) {
switch (*from) {
case '/':
case '@':
case ':':
*to = '\0';
return (from);
default:
*to++ = *from++;
}
}
*to = '\0';
return (from);
}
struct _p2d {
char *name;
char *addrspec;
int unit;
struct dev_info *dev;
};
static int
path_findmatch(dip, arg)
struct dev_info *dip;
caddr_t arg;
{
struct _p2d *p2d = (struct _p2d *)arg;
int (*match_func)();
extern int obio_match();
/*
* "A Grail? But we already have one!"
*/
if (p2d->dev)
return ((int)(p2d->dev));
#ifdef DPRINTF
dprintf("\tchecking %s\n", dip->devi_name);
#endif DPRINTF
/*
* "These aren't the droids you're looking for. Move along."
*/
if ((strcmp(p2d->name, "sd") == 0) &&
(strcmp(dip->devi_name, "sr") == 0))
goto sr_hack;
if (strcmp(dip->devi_name, p2d->name) != 0)
return (0);
sr_hack:
/*
* XXX - If no unit is specified, take the first.
* There should be some concept of "take the default".
*/
if (p2d->addrspec[0] == '\0')
return ((int)(p2d->dev = dip));
/*
* We may need to call a matching function to decode the address.
* First find out if there even is one. Note that the address encoding
* is a function of our *parent*.
*/
match_func = path_getmatchfunc(dip->devi_parent);
/*
* XXX - No matching function? Use "obio" by default.
*/
if (match_func == (int (*)())0)
match_func = obio_match;
if (match_func(dip, p2d->addrspec))
return ((int)(p2d->dev = dip));
return ((int)(p2d->dev = (struct dev_info *)0));
}
static struct dev_info *
path_matchtoken(name, addrspec, dip)
char *name, *addrspec;
struct dev_info *dip;
{
struct _p2d p2d;
p2d.name = name;
p2d.addrspec = addrspec;
p2d.dev = NULL;
#ifdef DPRINTF
dprintf("walk layer at %x (%s); looking for '%s' '%s'\n",
dip, dip->devi_name, name, addrspec);
#endif DPRINTF
walk_layer(dip->devi_slaves, path_findmatch, (caddr_t)&p2d);
return (p2d.dev);
}
/*
* Given a path specifying a node in the prom's device tree,
* return the kernel's corresponding dev_info node.
* XXX - doesn't handle units yet.
*/
struct dev_info *
path_to_devi(p)
char *p;
{
char name[128], addrspec[128];
struct dev_info *dip = top_devinfo;
if (!p)
return ((struct dev_info *)0);
while (*p && *p == '/')
++p;
#ifdef DPRINTF
dprintf("path: '%s'\n", p);
#endif DPRINTF
while (*p) {
p = path_gettoken(p, name);
#ifdef DPRINTF
dprintf("name: '%s' remainder: '%s'\n", name, p);
#endif DPRINTF
if (*p == '@')
p = path_gettoken(++p, addrspec);
else
addrspec[0] = '\0';
#ifdef DPRINTF
dprintf("addrspec: '%s' remainder: '%s'\n", addrspec, p);
#endif DPRINTF
dip = path_matchtoken(name, addrspec, dip);
if (dip == (struct dev_info *)0)
break;
while (*p && *p++ != '/')
;
}
return (dip);
}
dnode_t
path_to_nodeid(p)
char *p;
{
struct dev_info *dip;
dip = path_to_devi(p);
return (dip ? (dnode_t)dip->devi_nodeid : OBP_BADNODE);
}
/*
* path_to_subdev: return the subdevice of the given
* path, defined as the string following the *FINAL*
* colon in the path.
*
* %%% if we want to use more than one colon in a
* path, or strange semantics of colons earlier in
* the path than the final component, fix this.
*/
char *
path_to_subdev(p)
char *p;
{
register char ch;
while (ch = *p++)
if (ch == ':')
return (p);
return ((char *)0);
}
/*
* Given a path "/foo/bar/mumble/disk@blah,blah:part" where
* part is an integer, return the partition number.
* XXX - This needs to be designed better. Should part be a block offset,
* or an integer, rather than a character?
*/
int
get_part_from_path(p)
char *p;
{
char *cp;
if (!p)
return (0);
cp = p + strlen(p);
while (--cp != p) {
if (*cp == ':')
break;
}
if ((cp++ == p) || (*cp == '\0'))
return (0);
if (*cp >= 'a' && *cp <= 'z') /* letter */
return (*cp - 'a');
if (*cp >= '0' && *cp <= '9') /* digit */
return (*cp - '0');
return (*cp); /* raw integer? */
}
static int
path_matchnameunit(dip, arg)
struct dev_info *dip;
caddr_t arg;
{
struct _p2d *p2d = (struct _p2d *)arg;
if (p2d->dev)
return ((int)(p2d->dev));
#ifdef DPRINTF
dprintf("\tchecking devi <%x> unit <%d> name <%s>\n",
(int)dip, (int)dip->devi_unit, dip->devi_name);
#endif DPRINTF
if (strcmp(dip->devi_name, p2d->name) != 0)
return (0);
/*
* XXX - If no unit is specified, take the first.
* There should be some concept of "take the default".
*/
if (p2d->unit == -1 || p2d->unit == dip->devi_unit)
return ((int)(p2d->dev = dip));
return ((int)(p2d->dev = (struct dev_info *)0));
}
static struct dev_info *
path_findnodebyname(name, unit, dip)
char *name;
int unit;
struct dev_info *dip;
{
struct _p2d p2d;
p2d.name = name;
p2d.unit = unit;
p2d.dev = NULL;
#ifdef DPRINTF
dprintf("walk tree at %x (%s); looking for '%s' '%d'\n",
dip, dip->devi_name, name, unit);
#endif DPRINTF
walk_devs(dip, path_matchnameunit, (caddr_t)&p2d);
return (p2d.dev);
}
void
devi_to_path(name)
char *name;
{
struct dev_info *dip, *pdip;
char tmpname[OBP_MAXDEVNAME];
char addr[OBP_MAXDEVNAME];
char *cp;
int unit = 0;
int base = 10;
char part = '\0';
extern char *strcpy();
char *(*encode_func)();
/*
* Scan "name", pulling out the unit number and partition.
* Null out the unit number in the string so that the device
* name becomes null-terminated.
*/
cp = name;
#ifndef sun4c
/*
* Special case (for IPI) if device unit number is in hex.
* Hex unit numbers are three digits, currently...
*/
if (units_are_hex(cp) != 0) {
if (*(cp+5))
part = *(cp+5);
*(cp+5) = (char)0;
unit = atou(cp+2);
*(cp+2) = (char)0;
} else {
#endif ! sun4c
while (*cp && *cp > '9')
++cp;
while (*cp) {
if ((*cp < '0') || (*cp > '9'))
break;
unit = (unit * base) + (*cp - '0');
*cp++ = '\0';
}
if (*cp)
part = *cp;
#ifndef sun4c
}
#endif ! sun4c
/*
* Locate the devinfo node for this device.
*/
dip = path_findnodebyname(name, unit, top_devinfo);
#ifdef DPRINTF
dprintf("path_to_devi: path_findnodebyname returned dip %x\n",
(int)dip);
#endif DPRINTF
*name = '\0';
if (dip == (struct dev_info *)0)
return;
#ifdef DPRINTF
dprintf("path_to_devi: devi_name %s, devi_parent is %x\n",
dip->devi_name, dip->devi_parent);
#endif DPRINTF
/*
* Now trace the tree bottom-to-top, translating each node
* into a prom-compatible specifier. Prepend each specifier
* to the path already built. Stop at the root node.
*/
for (/* empty */; ((pdip = dip->devi_parent) != (struct dev_info *)0);
dip = pdip) {
#ifndef lint
register char *p;
#endif
/*
* Find the appropriate encoding funtion to translate
* the node's reg, target/lun, etc. information
* to an address specification that the prom can grok.
*/
encode_func = path_getencodefunc(dip->devi_parent);
/*
* XXX - No matching function? Use "obio" by default.
*/
if (encode_func == (char *(*)())0)
encode_func = obio_encode_reg;
switch (encode_func(dip, addr)) {
case 0:
/*
* Error return: nullify output buffer so
* caller notices error... (error if output
* does not start with a '/' character.)
*/
*name = (char)0;
return;
case -1:
/*
* Skip me -- put nothing in output buffer.
* Node must be a software only node.
* Move along to the next node...
*/
continue;
case -2:
/*
* Full name returned -- just take it with a slash
* and ignore dip->devi_name.
*/
if (*addr != (char)0)
(void)sprintf(tmpname, "/%s%s",
addr, name);
break;
default:
/*
* If output buffer is NULL, there is no
* address qualifier, else there is one.
*/
if (*addr != (char)0)
(void)sprintf(tmpname, "/%s@%s%s",
dip->devi_name, addr, name);
else
(void)sprintf(tmpname, "/%s%s",
dip->devi_name, name);
}
(void)strcpy(name, tmpname);
#ifdef DPRINTF
dprintf("***name <%s>\n", name);
#endif DPRINTF
}
if (part) {
cp = name + strlen(name);
(void)sprintf(cp, ":%c", part);
}
#ifdef DPRINTF
dprintf("name = '%s'\n", name);
#endif DPRINTF
}
#ifndef sun4c
static int
units_are_hex(name)
register char *name;
{
register char **p;
extern char *hex_unit_devices[];
for (p = hex_unit_devices; *p != (char *)0; ++p) {
if (strncmp(*p, name, 2) == 0) {
#ifdef DPRINTF
dprintf("%s units are in hex.\n");
#endif DPRINTF
return (1);
}
}
#ifdef DPRINTF
dprintf("%s units are not in hex.\n");
#endif DPRINTF
return (0);
}
#endif ! sun4c