[BACK]Return to autoconf.c CVS log [TXT][DIR] Up to [local] / sys / arch / sparc64 / sparc64

File: [local] / sys / arch / sparc64 / sparc64 / autoconf.c (download)

Revision 1.1, Tue Mar 4 16:08:20 2008 UTC (16 years, 2 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*	$OpenBSD: autoconf.c,v 1.69 2007/08/07 21:20:54 kettenis Exp $	*/
/*	$NetBSD: autoconf.c,v 1.51 2001/07/24 19:32:11 eeh Exp $ */

/*
 * Copyright (c) 1996
 *    The President and Fellows of Harvard College. All rights reserved.
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by Harvard University.
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)autoconf.c	8.4 (Berkeley) 10/1/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/disklabel.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/dkstat.h>
#include <sys/conf.h>
#include <sys/reboot.h>
#include <sys/socket.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/msgbuf.h>

#include <net/if.h>

#include <dev/cons.h>

#include <uvm/uvm_extern.h>

#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/openfirm.h>
#include <machine/sparc64.h>
#include <machine/cpu.h>
#include <machine/pmap.h>
#include <sparc64/sparc64/timerreg.h>

#include <dev/ata/atavar.h>
#include <dev/pci/pcivar.h>
#include <dev/sbus/sbusvar.h>

#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>

#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#endif

int printspl = 0;

/*
 * The following several variables are related to
 * the configuration process, and are used in initializing
 * the machine.
 */
int	stdinnode;	/* node ID of ROM's console input device */
int	fbnode;		/* node ID of ROM's console output device */
int	optionsnode;	/* node ID of ROM's options */

#ifdef KGDB
extern	int kgdb_debug_panic;
#endif

static	int rootnode;
char platform_type[64];

/* for hw.product/vendor see sys/kern/kern_sysctl.c */
extern char *hw_prod, *hw_vendor;

static	char *str2hex(char *, long *);
static	int mbprint(void *, const char *);
void	sync_crash(void);
int	mainbus_match(struct device *, void *, void *);
static	void mainbus_attach(struct device *, struct device *, void *);

struct device *booted_device;
struct	bootpath bootpath[8];
int	nbootpath;
int	bootnode;
static	void bootpath_build(void);
static	void bootpath_print(struct bootpath *);
void bootpath_nodes(struct bootpath *, int);

int bus_class(struct device *);
int instance_match(struct device *, void *, struct bootpath *bp);
void nail_bootdev(struct device *, struct bootpath *);

/* Global interrupt mappings for all device types.  Match against the OBP
 * 'device_type' property. 
 */
struct intrmap intrmap[] = {
	{ "block",	PIL_FD },	/* Floppy disk */
	{ "serial",	PIL_SER },	/* zs */
	{ "scsi",	PIL_SCSI },
	{ "scsi-2",	PIL_SCSI },
	{ "network",	PIL_NET },
	{ "display",	PIL_VIDEO },
	{ "audio",	PIL_AUD },
	{ "ide",	PIL_SCSI },
/* The following devices don't have device types: */
	{ "SUNW,CS4231",	PIL_AUD },
	{ NULL,		0 }
};


#ifdef DEBUG
#define ACDB_BOOTDEV	0x1
#define	ACDB_PROBE	0x2
int autoconf_debug = 0x0;
#define DPRINTF(l, s)   do { if (autoconf_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif

/*
 * Convert hex ASCII string to a value.  Returns updated pointer.
 * Depends on ASCII order (this *is* machine-dependent code, you know).
 */
static char *
str2hex(char *str, long *vp)
{
	long v;
	int c;

	if (*str == 'w') {
		for (v = 1;; v++) {
			if (str[v] >= '0' && str[v] <= '9')
				continue;
			if (str[v] >= 'a' && str[v] <= 'f')
				continue;
			if (str[v] >= 'A' && str[v] <= 'F')
				continue;
			if (str[v] == '\0' || str[v] == ',')
				break;
			*vp = 0;
			return (str + v);
		}
		str++;
	}

	for (v = 0;; v = v * 16 + c, str++) {
		c = *(u_char *)str;
		if (c <= '9') {
			if ((c -= '0') < 0)
				break;
		} else if (c <= 'F') {
			if ((c -= 'A' - 10) < 10)
				break;
		} else if (c <= 'f') {
			if ((c -= 'a' - 10) < 10)
				break;
		} else
			break;
	}
	*vp = v;
	return (str);
}

/*
 * locore.s code calls bootstrap() just before calling main().
 *
 * What we try to do is as follows:
 *
 * 1) We will try to re-allocate the old message buffer.
 *
 * 2) We will then get the list of the total and available
 *	physical memory and available virtual memory from the
 *	prom.
 *
 * 3) We will pass the list to pmap_bootstrap to manage them.
 *
 * We will try to run out of the prom until we get to cpu_init().
 */
void
bootstrap(nctx)
	int nctx;
{
	extern int end;	/* End of kernel */

	/* 
	 * Initialize ddb first and register OBP callbacks.
	 * We can do this because ddb_init() does not allocate anything,
	 * just initializes some pointers to important things
	 * like the symtab.
	 *
	 * By doing this first and installing the OBP callbacks
	 * we get to do symbolic debugging of pmap_bootstrap().
	 */
#ifdef KGDB
/* Moved zs_kgdb_init() to dev/zs.c:consinit(). */
	zs_kgdb_init();		/* XXX */
#endif
	/* Initialize the PROM console so printf will not panic */
	(*cn_tab->cn_init)(cn_tab);
#ifdef DDB
	db_machine_init();
	ddb_init();
	/* This can only be installed on an 64-bit system cause otherwise our stack is screwed */
	OF_set_symbol_lookup(OF_sym2val, OF_val2sym);
#endif

	pmap_bootstrap(KERNBASE, (u_long)&end, nctx);
}

void
bootpath_nodes(struct bootpath *bp, int nbp)
{
	int chosen;
	int i;
	char buf[128], *cp, c;

	chosen = OF_finddevice("/chosen");
	OF_getprop(chosen, "bootpath", buf, sizeof(buf));
	cp = buf;

	for (i = 0; i < nbp; i++, bp++) {
		if (*cp == '\0')
			return;
		while (*cp != '\0' && *cp == '/')
			cp++;
		while (*cp && *cp != '/')
			cp++;
		c = *cp;
		*cp = '\0';
		bootnode = bp->node = OF_finddevice(buf);
		*cp = c;
	}
}

/*
 * bootpath_build: build a bootpath. Used when booting a generic
 * kernel to find our root device.  Newer proms give us a bootpath,
 * for older proms we have to create one.  An element in a bootpath
 * has 4 fields: name (device name), val[0], val[1], and val[2]. Note that:
 * Interpretation of val[] is device-dependent. Some examples:
 *
 * if (val[0] == -1) {
 *	val[1] is a unit number    (happens most often with old proms)
 * } else {
 *	[sbus device] val[0] is a sbus slot, and val[1] is an sbus offset
 *	[scsi disk] val[0] is target, val[1] is lun, val[2] is partition
 *	[scsi tape] val[0] is target, val[1] is lun, val[2] is file #
 *	[pci device] val[0] is device, val[1] is function, val[2] might be partition
 * }
 *
 */

static void
bootpath_build()
{
	register char *cp, *pp;
	register struct bootpath *bp;
	register long chosen;
	char buf[128];

	bzero((void *)bootpath, sizeof(bootpath));
	bp = bootpath;

	/*
	 * Grab boot path from PROM
	 */
	chosen = OF_finddevice("/chosen");
	OF_getprop(chosen, "bootpath", buf, sizeof(buf));
	cp = buf;
	while (cp != NULL && *cp == '/') {
		/* Step over '/' */
		++cp;
		/* Extract name */
		pp = bp->name;
		while (*cp != '@' && *cp != '/' && *cp != '\0')
			*pp++ = *cp++;
		*pp = '\0';
		if (*cp == '@') {
			cp = str2hex(++cp, &bp->val[0]);
			if (*cp == ',')
				cp = str2hex(++cp, &bp->val[1]);
			if (*cp == ':') {
				/*
				 * We only store one character here, as we will
				 * only use this field to compute a partition
				 * index for block devices.  However, it might
				 * be an ethernet media specification, so be
				 * sure to skip all letters.
				 */
				bp->val[2] = *++cp - 'a';
				while (*cp != '\0' && *cp != '/')
					cp++;
			}
		} else {
			bp->val[0] = -1; /* no #'s: assume unit 0, no
					    sbus offset/address */
		}
		++bp;
		++nbootpath;
	}
	bp->name[0] = 0;
	
	bootpath_nodes(bootpath, nbootpath);
	
	/* Setup pointer to boot flags */
	OF_getprop(chosen, "bootargs", buf, sizeof(buf));
	cp = buf;

	/* Find start of boot flags */
	while (*cp) {
		while(*cp == ' ' || *cp == '\t') cp++;
		if (*cp == '-' || *cp == '\0')
			break;
		while(*cp != ' ' && *cp != '\t' && *cp != '\0') cp++;
		
	}
	if (*cp != '-')
		return;

	for (;*++cp;) {
		int fl;

		fl = 0;
		switch(*cp) {
		case 'a':
			fl |= RB_ASKNAME;
			break;
		case 'b':
			fl |= RB_HALT;
			break;
		case 'c':
			fl |= RB_CONFIG;
			break;
		case 'd':
			fl |= RB_KDB;
			break;
		case 's':
			fl |= RB_SINGLE;
			break;
		default:
			break;
		}
		if (!fl) {
			printf("unknown option `%c'\n", *cp);
			continue;
		}
		boothowto |= fl;

		/* specialties */
		if (*cp == 'd') {
#if defined(KGDB)
			kgdb_debug_panic = 1;
			kgdb_connect(1);
#elif defined(DDB)
			Debugger();
#else
			printf("kernel has no debugger\n");
#endif
		} else if (*cp == 't') {
			/* turn on traptrace w/o breaking into kdb */
			extern int trap_trace_dis;

			trap_trace_dis = 0;
		}
	}
}

/*
 * print out the bootpath
 * the %x isn't 0x%x because the Sun EPROMs do it this way, and
 * consistency with the EPROMs is probably better here.
 */

static void
bootpath_print(bp)
	struct bootpath *bp;
{
	printf("bootpath: ");
	while (bp->name[0]) {
		if (bp->val[0] == -1)
			printf("/%s%x", bp->name, bp->val[1]);
		else
			printf("/%s@%lx,%lx", bp->name, bp->val[0], bp->val[1]);
		if (bp->val[2] != 0)
			printf(":%c", bp->val[2] + 'a');
		bp++;
	}
	printf("\n");
}


/*
 * save or read a bootpath pointer from the boothpath store.
 *
 * XXX. required because of SCSI... we don't have control over the "sd"
 * device, so we can't set boot device there.   we patch in with
 * device_register(), and use this to recover the bootpath.
 */
struct bootpath *
bootpath_store(storep, bp)
	int storep;
	struct bootpath *bp;
{
	static struct bootpath *save;
	struct bootpath *retval;

	retval = save;
	if (storep)
		save = bp;

	return (retval);
}

/*
 * Determine mass storage and memory configuration for a machine.
 * We get the PROM's root device and make sure we understand it, then
 * attach it as `mainbus0'.  We also set up to handle the PROM `sync'
 * command.
 */
void
cpu_configure()
{
#if 0
	extern struct user *proc0paddr;	/* XXX see below */
#endif

	/* build the bootpath */
	bootpath_build();

	if (boothowto & RB_CONFIG) {
#ifdef BOOT_CONFIG
		user_config();
#else
		printf("kernel does not support -c; continuing..\n");
#endif
	}

#if notyet
        /* FIXME FIXME FIXME  This is probably *WRONG!!!**/
        OF_set_callback(sync_crash);
#endif

	/* block clock interrupts and anything below */
	splclock();
	/* Enable device interrupts */
        setpstate(getpstate()|PSTATE_IE);

	if (config_rootfound("mainbus", NULL) == NULL)
		panic("mainbus not configured");

	/* Enable device interrupts */
        setpstate(getpstate()|PSTATE_IE);

#if 0
	/*
	 * XXX Re-zero proc0's user area, to nullify the effect of the
	 * XXX stack running into it during auto-configuration.
	 * XXX - should fix stack usage.
	 */
	bzero(proc0paddr, sizeof(struct user));
#endif

	(void)spl0();
	cold = 0;
}

void
diskconf(void)
{
	struct bootpath *bp;
	struct device *bootdv;

	bootpath_print(bootpath);

	bp = nbootpath == 0 ? NULL : &bootpath[nbootpath-1];
	bootdv = (bp == NULL) ? NULL : bp->dev;

	setroot(bootdv, bp->val[2], RB_USERREQ | RB_HALT);
	dumpconf();
}

/*
 * Console `sync' command.  SunOS just does a `panic: zero' so I guess
 * no one really wants anything fancy...
 */
void
sync_crash()
{

	panic("PROM sync command");
}

char *
clockfreq(freq)
	long freq;
{
	char *p;
	static char buf[10];

	freq /= 1000;
	snprintf(buf, sizeof buf, "%ld", freq / 1000);
	freq %= 1000;
	if (freq) {
		freq += 1000;	/* now in 1000..1999 */
		p = buf + strlen(buf);
		snprintf(p, buf + sizeof buf - p, "%ld", freq);
		*p = '.';	/* now buf = %d.%3d */
	}
	return (buf);
}

/* ARGSUSED */
static int
mbprint(aux, name)
	void *aux;
	const char *name;
{
	struct mainbus_attach_args *ma = aux;

	if (name)
		printf("\"%s\" at %s", ma->ma_name, name);
	if (ma->ma_address)
		printf(" addr 0x%08lx", (u_long)ma->ma_address[0]);
	if (ma->ma_pri)
		printf(" ipl %d", ma->ma_pri);
	return (UNCONF);
}

int
findroot()
{
	register int node;

	if ((node = rootnode) == 0 && (node = OF_peer(0)) == 0)
		panic("no PROM root device");
	rootnode = node;
	return (node);
}

/*
 * Given a `first child' node number, locate the node with the given name.
 * Return the node number, or 0 if not found.
 */
int
findnode(first, name)
	int first;
	register const char *name;
{
	int node;
	char buf[32];

	for (node = first; node; node = OF_peer(node)) {
		if ((OF_getprop(node, "name", buf, sizeof(buf)) > 0) &&
			(strcmp(buf, name) == 0))
			return (node);
	}
	return (0);
}

int
mainbus_match(parent, cf, aux)
	struct device *parent;
	void *cf;
	void *aux;
{

	return (1);
}

/*
 * Attach the mainbus.
 *
 * Our main job is to attach the CPU (the root node we got in cpu_configure())
 * and iterate down the list of `mainbus devices' (children of that node).
 * We also record the `node id' of the default frame buffer, if any.
 */
static void
mainbus_attach(parent, dev, aux)
	struct device *parent, *dev;
	void *aux;
{
extern struct sparc_bus_dma_tag mainbus_dma_tag;
extern bus_space_tag_t mainbus_space_tag;

	struct mainbus_attach_args ma;
	char buf[32], *p;
	const char *const *ssp, *sp = NULL;
	int node0, node, rv, len;

	static const char *const openboot_special[] = {
		/* ignore these (end with NULL) */
		/*
		 * These are _root_ devices to ignore. Others must be handled
		 * elsewhere.
		 */
		"virtual-memory",
		"aliases",
		"memory",
		"openprom",
		"options",
		"packages",
		"chosen",
		NULL
	};

	if ((len = OF_getprop(findroot(), "banner-name", platform_type,
	    sizeof(platform_type))) <= 0)
		OF_getprop(findroot(), "name", platform_type,
		    sizeof(platform_type));
	printf(": %s\n", platform_type);

	hw_vendor = malloc(sizeof(platform_type), M_DEVBUF, M_NOWAIT);
	if (len > 0 && hw_vendor != NULL) {
		strlcpy(hw_vendor, platform_type, sizeof(platform_type));
		if ((strncmp(hw_vendor, "SUNW,", 5)) == 0) {
			p = hw_prod = hw_vendor + 5;
			hw_vendor = "Sun";
		} else if ((p = memchr(hw_vendor, ' ', len)) != NULL) {
			*p = '\0';
			hw_prod = ++p;
		}
		if ((p = memchr(hw_prod, '(', len - (p - hw_prod))) != NULL)
			*p = '\0';
	}

	/*
	 * Locate and configure the ``early'' devices.  These must be
	 * configured before we can do the rest.  For instance, the
	 * EEPROM contains the Ethernet address for the LANCE chip.
	 * If the device cannot be located or configured, panic.
	 */

/*
 * The rest of this routine is for OBP machines exclusively.
 */

	node = findroot();

	/* Establish the first component of the boot path */
	bootpath_store(1, bootpath);

	/* the first early device to be configured is the cpu */

	{
		/* 
		 * UltraSPARC-IV cpus appear as two "cpu" nodes below
		 * a "cmp" node.  Lookup the first "cmp" node, such
		 * that we find the "cpu" node in the code below.
		 */

		for (node = OF_child(node); node; node = OF_peer(node)) {
			if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
				continue;
			if (strcmp(buf, "cmp") == 0)
				break;
		}

		if (node == 0)
			node = findroot();
	}

	{
		/* XXX - what to do on multiprocessor machines? */

		for (node = OF_child(node); node; node = OF_peer(node)) {
			if (OF_getprop(node, "device_type", 
				buf, sizeof(buf)) <= 0)
				continue;
			if (strcmp(buf, "cpu") == 0) {
				bzero(&ma, sizeof(ma));
				ma.ma_bustag = mainbus_space_tag;
				ma.ma_dmatag = &mainbus_dma_tag;
				ma.ma_node = node;
				ma.ma_name = "cpu";
				config_found(dev, (void *)&ma, mbprint);
				break;
			}
		}
		if (node == 0)
			panic("None of the CPUs found");
	}


	node = findroot();	/* re-init root node */

	/* Find the "options" node */
	node0 = OF_child(node);
	optionsnode = findnode(node0, "options");
	if (optionsnode == 0)
		panic("no options in OPENPROM");

	/*
	 * Configure the devices, in PROM order.  Skip
	 * PROM entries that are not for devices, or which must be
	 * done before we get here.
	 */
	for (node = node0; node; node = OF_peer(node)) {
		int portid;

		DPRINTF(ACDB_PROBE, ("Node: %x", node));
		if ((OF_getprop(node, "device_type", buf, sizeof(buf)) > 0) &&
			strcmp(buf, "cpu") == 0)
			continue;
		OF_getprop(node, "name", buf, sizeof(buf));
		DPRINTF(ACDB_PROBE, (" name %s\n", buf));
		for (ssp = openboot_special; (sp = *ssp) != NULL; ssp++)
			if (strcmp(buf, sp) == 0)
				break;
		if (sp != NULL)
			continue; /* an "early" device already configured */

		bzero(&ma, sizeof ma);
		ma.ma_bustag = mainbus_space_tag;
		ma.ma_dmatag = &mainbus_dma_tag;
		ma.ma_name = buf;
		ma.ma_node = node;
		if (OF_getprop(node, "upa-portid", &portid, sizeof(portid)) !=
		    sizeof(portid)) {
			if (OF_getprop(node, "portid", &portid,
			    sizeof(portid)) != sizeof(portid))
				portid = -1;
		}
		ma.ma_upaid = portid;

		if (getprop(node, "reg", sizeof(*ma.ma_reg), 
			     &ma.ma_nreg, (void **)&ma.ma_reg) != 0)
			continue;
#ifdef DEBUG
		if (autoconf_debug & ACDB_PROBE) {
			if (ma.ma_nreg)
				printf(" reg %08lx.%08lx\n",
					(long)ma.ma_reg->ur_paddr, 
					(long)ma.ma_reg->ur_len);
			else
				printf(" no reg\n");
		}
#endif
		rv = getprop(node, "interrupts", sizeof(*ma.ma_interrupts), 
			&ma.ma_ninterrupts, (void **)&ma.ma_interrupts);
		if (rv != 0 && rv != ENOENT) {
			free(ma.ma_reg, M_DEVBUF);
			continue;
		}
#ifdef DEBUG
		if (autoconf_debug & ACDB_PROBE) {
			if (ma.ma_interrupts)
				printf(" interrupts %08x\n", 
					*ma.ma_interrupts);
			else
				printf(" no interrupts\n");
		}
#endif
		rv = getprop(node, "address", sizeof(*ma.ma_address), 
			&ma.ma_naddress, (void **)&ma.ma_address);
		if (rv != 0 && rv != ENOENT) {
			free(ma.ma_reg, M_DEVBUF);
			if (ma.ma_ninterrupts)
				free(ma.ma_interrupts, M_DEVBUF);
			continue;
		}
#ifdef DEBUG
		if (autoconf_debug & ACDB_PROBE) {
			if (ma.ma_naddress)
				printf(" address %08x\n", 
					*ma.ma_address);
			else
				printf(" no address\n");
		}
#endif
		(void) config_found(dev, (void *)&ma, mbprint);
		free(ma.ma_reg, M_DEVBUF);
		if (ma.ma_ninterrupts)
			free(ma.ma_interrupts, M_DEVBUF);
		if (ma.ma_naddress)
			free(ma.ma_address, M_DEVBUF);
	}
	/* Try to attach PROM console */
	bzero(&ma, sizeof ma);
	ma.ma_name = "pcons";
	(void) config_found(dev, (void *)&ma, mbprint);
}

struct cfattach mainbus_ca = {
	sizeof(struct device), mainbus_match, mainbus_attach
};

int
getprop(node, name, size, nitem, bufp)
	int	node;
	char	*name;
	size_t	size;
	int	*nitem;
	void	**bufp;
{
	void	*buf;
	long	len;

	*nitem = 0;
	len = getproplen(node, name);
	if (len <= 0)
		return (ENOENT);

	if ((len % size) != 0)
		return (EINVAL);

	buf = *bufp;
	if (buf == NULL) {
		/* No storage provided, so we allocate some */
		buf = malloc(len, M_DEVBUF, M_NOWAIT);
		if (buf == NULL)
			return (ENOMEM);
	}

	OF_getprop(node, name, buf, len);
	*bufp = buf;
	*nitem = len / size;
	return (0);
}


/*
 * Internal form of proplen().  Returns the property length.
 */
long
getproplen(node, name)
	int node;
	char *name;
{
	return (OF_getproplen(node, name));
}

/*
 * Return a string property.  There is a (small) limit on the length;
 * the string is fetched into a static buffer which is overwritten on
 * subsequent calls.
 */
char *
getpropstring(node, name)
	int node;
	char *name;
{
	static char stringbuf[32];

	return (getpropstringA(node, name, stringbuf));
}

/* Alternative getpropstring(), where caller provides the buffer */
char *
getpropstringA(node, name, buffer)
	int node;
	char *name;
	char *buffer;
{
	int blen;

	if (getprop(node, name, 1, &blen, (void **)&buffer) != 0)
		blen = 0;

	buffer[blen] = '\0';	/* usually unnecessary */
	return (buffer);
}

/*
 * Fetch an integer (or pointer) property.
 * The return value is the property, or the default if there was none.
 */
int
getpropint(node, name, deflt)
	int node;
	char *name;
	int deflt;
{
	int intbuf;

	

	if (OF_getprop(node, name, &intbuf, sizeof(intbuf)) != sizeof(intbuf))
		return (deflt);

	return (intbuf);
}

/*
 * OPENPROM functions.  These are here mainly to hide the OPENPROM interface
 * from the rest of the kernel.
 */
int
firstchild(node)
	int node;
{

	return OF_child(node);
}

int
nextsibling(node)
	int node;
{

	return OF_peer(node);
}

/* The following are used primarily in consinit() */

int
node_has_property(node, prop)	/* returns 1 if node has given property */
	register int node;
	register const char *prop;
{
	return (OF_getproplen(node, (caddr_t)prop) != -1);
}

/*
 * Try to figure out where the PROM stores the cursor row & column
 * variables.  Returns nonzero on error.
 */
int
romgetcursoraddr(rowp, colp)
	int **rowp, **colp;
{
	cell_t row = NULL, col = NULL;

	OF_interpret("stdout @ is my-self addr line# addr column# ",
	    2, &col, &row);

	/*
	 * We are running on a 64-bit machine, so these things point to
	 * 64-bit values.  To convert them to pointers to interfaces, add
	 * 4 to the address.
	 */
	if (row == NULL || col == NULL)
		return (-1);
	*rowp = (int *)(row + 4);
	*colp = (int *)(col + 4);
	return (0);
}

void
callrom()
{

	__asm __volatile("wrpr	%%g0, 0, %%tl" : );
	OF_enter();
}

/*
 * find a device matching "name" and unit number
 */
struct device *
getdevunit(name, unit)
	char *name;
	int unit;
{
	struct device *dev = TAILQ_FIRST(&alldevs);
	char num[10], fullname[16];
	int lunit;

	/* compute length of name and decimal expansion of unit number */
	snprintf(num, sizeof num, "%d", unit);
	lunit = strlen(num);
	if (strlen(name) + lunit >= sizeof(fullname) - 1)
		panic("config_attach: device name too long");

	strlcpy(fullname, name, sizeof fullname);
	strlcat(fullname, num, sizeof fullname);

	while (strcmp(dev->dv_xname, fullname) != 0) {
		if ((dev = TAILQ_NEXT(dev, dv_list)) == NULL)
			return NULL;
	}
	return dev;
}

void
device_register(struct device *dev, void *aux)
{
	struct mainbus_attach_args *ma = aux;
	struct pci_attach_args *pa = aux;
	struct sbus_attach_args *sa = aux;
	struct bootpath *bp = bootpath_store(0, NULL);
	struct device *busdev = dev->dv_parent;
	const char *devname = dev->dv_cfdata->cf_driver->cd_name;
	const char *busname;
	int node = -1;

	/*
	 * There is no point in continuing if we've exhausted all
	 * bootpath components.
	 */
	if (bp == NULL)
		return;

	DPRINTF(ACDB_BOOTDEV,
	    ("\n%s: device_register: devname %s(%s) component %s\n",
	    dev->dv_xname, devname, dev->dv_xname, bp->name));

	/*
	 * Ignore mainbus0 itself, it certainly is not a boot device.
	 */
	if (busdev == NULL)
		return;

	/*
	 * We don't know the type of 'aux'; it depends on the bus this
	 * device attaches to.  We are only interested in certain bus
	 * types; this is only used to find the boot device.
	 */
	busname = busdev->dv_cfdata->cf_driver->cd_name;
	if (strcmp(busname, "mainbus") == 0 || strcmp(busname, "upa") == 0)
		node = ma->ma_node;
	else if (strcmp(busname, "sbus") == 0 ||
	    strcmp(busname, "dma") == 0 || strcmp(busname, "ledma") == 0)
		node = sa->sa_node;
	else if (strcmp(busname, "pci") == 0)
		node = PCITAG_NODE(pa->pa_tag);

	if (node == bootnode) {
		nail_bootdev(dev, bp);
		return;
	}

	if (node == bp->node) {
		bp->dev = dev;
		DPRINTF(ACDB_BOOTDEV, ("\t-- matched component %s to %s\n",
		    bp->name, dev->dv_xname));
		bootpath_store(1, bp + 1);
		return;
	}

	if (strcmp(devname, "scsibus") == 0) {
		struct scsi_link *sl = aux;

		if (strcmp(bp->name, "fp") == 0 &&
		    bp->val[0] == sl->scsibus) {
			DPRINTF(ACDB_BOOTDEV, ("\t-- matched component %s to %s\n",
			    bp->name, dev->dv_xname));
			bootpath_store(1, bp + 1);
			return;
		}
	}

	if (strcmp(devname, "sd") == 0 || strcmp(devname, "cd") == 0) {
		/*
		 * A SCSI disk or cd; retrieve target/lun information
		 * from parent and match with current bootpath component.
		 * Note that we also have look back past the `scsibus'
		 * device to determine whether this target is on the
		 * correct controller in our boot path.
		 */
		struct scsi_attach_args *sa = aux;
		struct scsi_link *sl = sa->sa_sc_link;
		struct scsibus_softc *sbsc =
		    (struct scsibus_softc *)dev->dv_parent;
		u_int target = bp->val[0];
		u_int lun = bp->val[1];

		if (bp->val[0] & 0xffffffff00000000 && bp->val[0] != -1) {
			/* Fibre channel? */
			if (bp->val[0] == sl->port_wwn && lun == sl->lun) {
				nail_bootdev(dev, bp);
			}
			return;
		}

		/* Check the controller that this scsibus is on. */
		if ((bp-1)->dev != sbsc->sc_dev.dv_parent)
			return;

		/*
		 * Bounds check: we know the target and lun widths.
		 */
		if (target >= sl->adapter_buswidth ||
		    lun >= sl->luns) {
			printf("SCSI disk bootpath component not accepted: "
			       "target %u; lun %u\n", target, lun);
			return;
		}

		if (target == sl->target && lun == sl->lun) {
			nail_bootdev(dev, bp);
			return;
		}
	}

	if (strcmp("wd", devname) == 0) {
		/* IDE disks. */
		struct ata_atapi_attach *aa = aux;
		u_int channel, drive;

		if (strcmp(bp->name, "ata") == 0 &&
		    bp->val[0] == aa->aa_channel) {
			channel = bp->val[0]; bp++;
			drive = bp->val[0];
		} else {
			channel = bp->val[0] / 2;
			drive = bp->val[0] % 2;
		}

		if (channel == aa->aa_channel &&
		    drive == aa->aa_drv_data->drive) {
			nail_bootdev(dev, bp);
			return;
		}
	}
}

void
nail_bootdev(dev, bp)
	struct device *dev;
	struct bootpath *bp;
{

	if (bp->dev != NULL)
		panic("device_register: already got a boot device: %s",
			bp->dev->dv_xname);

	/*
	 * Mark this bootpath component by linking it to the matched
	 * device. We pick up the device pointer in cpu_rootconf().
	 */
	booted_device = bp->dev = dev;
	DPRINTF(ACDB_BOOTDEV, ("\t-- found bootdevice: %s\n",dev->dv_xname));

	/*
	 * Then clear the current bootpath component, so we don't spuriously
	 * match similar instances on other busses, e.g. a disk on
	 * another SCSI bus with the same target.
	 */
	bootpath_store(1, NULL);
}

struct nam2blk nam2blk[] = {
	{ "sd",		 7 },
	{ "rd",		 5 },
	{ "wd",		12 },
	{ "cd",		18 },
	{ "raid",	25 },
	{ NULL,		-1 }
};