1/*	$NetBSD: promlib.c,v 1.42 2010/01/21 11:40:01 martin Exp $ */
2
3/*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Kranenburg.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * OPENPROM functions.  These are here mainly to hide the OPENPROM interface
34 * from the rest of the kernel.
35 */
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: promlib.c,v 1.42 2010/01/21 11:40:01 martin Exp $");
39
40#if defined(_KERNEL_OPT)
41#include "opt_sparc_arch.h"
42#endif
43
44#include <sys/param.h>
45#include <sys/kernel.h>
46
47#ifdef _STANDALONE
48#include <lib/libsa/stand.h>
49#define malloc(s,t,f)	alloc(s)
50#else
51#include <sys/systm.h>
52#include <sys/malloc.h>
53#endif /* _STANDALONE */
54
55#include <machine/oldmon.h>
56#include <machine/promlib.h>
57#include <machine/ctlreg.h>
58#include <sparc/sparc/asm.h>
59
60#include <lib/libkern/libkern.h>
61
62#define obpvec ((struct promvec *)romp)
63
64static void	notimplemented(void);
65static void	obp_v0_fortheval(const char *);
66static void	obp_set_callback(void (*)(void));
67static int	obp_v0_read(int, void *, int);
68static int	obp_v0_write(int, const void *, int);
69static int	obp_v2_getchar(void);
70static int	obp_v2_peekchar(void);
71static void	obp_v2_putchar(int);
72static void	obp_v2_putstr(const char *, int);
73static int	obp_v2_seek(int, u_quad_t);
74static char	*parse_bootfile(char *);
75static char	*parse_bootargs(char *);
76static const char *obp_v0_getbootpath(void);
77static const char *obp_v0_getbootfile(void);
78static const char *obp_v0_getbootargs(void);
79static const char *obp_v2_getbootpath(void);
80static const char *obp_v2_getbootfile(void);
81static const char *obp_v2_getbootargs(void);
82static int	obp_v2_finddevice(const char *);
83static int	obp_ticks(void);
84
85static int	findchosen(void);
86static const char *opf_getbootpath(void);
87static const char *opf_getbootfile(void);
88static const char *opf_getbootargs(void);
89static int	opf_finddevice(const char *);
90static int	opf_instance_to_package(int);
91static char	*opf_nextprop(int, const char *);
92static void	opf_interpret_simple(const char *);
93
94
95/*
96 * PROM entry points.
97 * Note: only PROM functions we use ar represented here; add as required.
98 */
99struct promops promops = {
100	-1,				/* version */
101	-1,				/* revision */
102	-1,				/* stdin handle */
103	-1,				/* stdout handle */
104	NULL,				/* bootargs */
105
106	(void *)notimplemented,		/* bootpath */
107	(void *)notimplemented,		/* bootargs */
108	(void *)notimplemented,		/* bootfile */
109
110	(void *)notimplemented,		/* getchar */
111	(void *)notimplemented,		/* peekchar */
112	(void *)notimplemented,		/* putchar */
113	(void *)notimplemented,		/* putstr */
114	(void *)notimplemented,		/* open */
115	(void *)notimplemented,		/* close */
116	(void *)notimplemented,		/* read */
117	(void *)notimplemented,		/* write */
118	(void *)notimplemented,		/* seek */
119
120	(void *)notimplemented,		/* instance_to_package */
121
122	(void *)notimplemented,		/* halt */
123	(void *)notimplemented,		/* boot */
124	(void *)notimplemented,		/* call */
125	(void *)notimplemented,		/* interpret */
126	(void *)notimplemented,		/* callback */
127	(void *)notimplemented,		/* ticks */
128	NULL,				/* ticker data */
129
130	(void *)notimplemented,		/* setcontext */
131	(void *)notimplemented,		/* cpustart */
132	(void *)notimplemented,		/* cpustop */
133	(void *)notimplemented,		/* cpuidle */
134	(void *)notimplemented,		/* cpuresume */
135
136	(void *)notimplemented,		/* firstchild */
137	(void *)notimplemented,		/* nextsibling */
138
139	(void *)notimplemented,		/* getproplen */
140	(void *)notimplemented,		/* getprop */
141	(void *)notimplemented,		/* setprop */
142	(void *)notimplemented,		/* nextprop */
143	(void *)notimplemented		/* finddevice */
144};
145
146static void
147notimplemented(void)
148{
149	char str[64];
150	int n;
151
152	n = sprintf(str, "Operation not implemented on ROM version %d\r\n",
153		    promops.po_version);
154
155	/*
156	 * Use PROM vector directly, in case we're called before prom_init().
157	 */
158#if defined(SUN4)
159	if (CPU_ISSUN4) {
160		struct om_vector *sun4pvec = (struct om_vector *)PROM_BASE;
161		(*sun4pvec->fbWriteStr)(str, n);
162	} else
163#endif
164	if (obpvec->pv_magic == OBP_MAGIC) {
165		if (obpvec->pv_romvec_vers < 2) {
166			(*obpvec->pv_putstr)(str, n);
167		} else {
168			int fd = *obpvec->pv_v2bootargs.v2_fd1;
169			(*obpvec->pv_v2devops.v2_write)(fd, str, n);
170		}
171	} else {	/* assume OFW */
172		static int stdout_node;
173		if (stdout_node == 0) {
174			int chosen = findchosen();
175			OF_getprop(chosen, "stdout", &stdout_node, sizeof(int));
176		}
177		OF_write(stdout_node, str, n);
178	}
179}
180
181
182/*
183 * prom_getprop() reads the named property data from a given node.
184 * A buffer for the data may be passed in `*bufp'; if NULL, a
185 * buffer is allocated. The argument `size' specifies the data
186 * element size of the property data. This function checks that
187 * the actual property data length is an integral multiple of
188 * the element size.  The number of data elements read into the
189 * buffer is returned into the integer pointed at by `nitem'.
190 */
191
192int
193prom_getprop(int node, const char *name, size_t	size, int *nitem, void *bufp)
194{
195	void	*buf;
196	int	len;
197
198	len = prom_getproplen(node, name);
199	if (len <= 0)
200		return (ENOENT);
201
202	if ((len % size) != 0)
203		return (EINVAL);
204
205	buf = *(void **)bufp;
206	if (buf == NULL) {
207		/* No storage provided, so we allocate some */
208		buf = malloc(len, M_DEVBUF, M_NOWAIT);
209		if (buf == NULL)
210			return (ENOMEM);
211	} else {
212		if (size * (*nitem) < len)
213			return (ENOMEM);
214	}
215
216	_prom_getprop(node, name, buf, len);
217	*(void **)bufp = buf;
218	*nitem = len / size;
219	return (0);
220}
221
222/*
223 * Return a string property.  There is a (small) limit on the length;
224 * the string is fetched into a static buffer which is overwritten on
225 * subsequent calls.
226 */
227char *
228prom_getpropstring(int node, const char *name)
229{
230	static char stringbuf[32];
231
232	return (prom_getpropstringA(node, name, stringbuf, sizeof stringbuf));
233}
234
235/*
236 * Alternative prom_getpropstring(), where caller provides the buffer
237 */
238char *
239prom_getpropstringA(int node, const char *name, char *buf, size_t bufsize)
240{
241	int len = bufsize - 1;
242
243	if (prom_getprop(node, name, 1, &len, &buf) != 0)
244		len = 0;
245
246	buf[len] = '\0';	/* usually unnecessary */
247	return (buf);
248}
249
250/*
251 * Fetch an integer (or pointer) property.
252 * The return value is the property, or the default if there was none.
253 */
254int
255prom_getpropint(int node, const char *name, int deflt)
256{
257	int intbuf, *ip = &intbuf;
258	int len = 1;
259
260	if (prom_getprop(node, name, sizeof(int), &len, &ip) != 0)
261		return (deflt);
262
263	return (*ip);
264}
265
266/*
267 * Node Name Matching per IEEE 1275, section 4.3.6.
268 */
269static int
270prom_matchname(int node, const char *name)
271{
272	char buf[32], *cp;
273
274	prom_getpropstringA(node, "name", buf, sizeof buf);
275	if (strcmp(buf, name) == 0)
276		/* Exact match */
277		return (1);
278
279	/* If name has a comma, an exact match is required */
280	if (strchr(name, ','))
281		return (0);
282
283	/*
284	 * Otherwise, if the node's name contains a comma, we can match
285	 * against the trailing string defined by the first comma.
286	 */
287	if ((cp = strchr(buf, ',')) != NULL) {
288		if (strcmp(cp + 1, name) == 0)
289			return (1);
290	}
291
292	return (0);
293}
294
295/*
296 * Translate device path to node
297 */
298int
299prom_opennode(const char *path)
300{
301	int fd;
302
303	if (prom_version() < 2) {
304		printf("WARNING: opennode not valid on PROM version %d\n",
305			promops.po_version);
306		return (0);
307	}
308	fd = prom_open(path);
309	if (fd == 0)
310		return (0);
311
312	return (prom_instance_to_package(fd));
313}
314
315int
316prom_findroot(void)
317{
318	static int rootnode;
319	int node;
320
321	if ((node = rootnode) == 0 && (node = prom_nextsibling(0)) == 0)
322		panic("no PROM root device");
323	rootnode = node;
324	return (node);
325}
326
327/*
328 * Given a `first child' node number, locate the node with the given name.
329 * Return the node number, or 0 if not found.
330 */
331int
332prom_findnode(int first, const char *name)
333{
334	int node;
335
336	for (node = first; node != 0; node = prom_nextsibling(node)) {
337		if (prom_matchname(node, name))
338			return (node);
339	}
340	return (0);
341}
342
343/*
344 * Determine whether a node has the given property.
345 */
346int
347prom_node_has_property(int node, const char *prop)
348{
349
350	return (prom_getproplen(node, prop) != -1);
351}
352
353/*
354 * prom_search() recursively searches a PROM subtree for a given node name
355 * See IEEE 1275 `Search for matching child node', section 4.3.3.
356 */
357int
358prom_search(int node, const char *name)
359{
360
361	if (node == 0)
362		node = prom_findroot();
363
364	if (prom_matchname(node, name))
365		return (node);
366
367	for (node = prom_firstchild(node); node != 0;
368	     node = prom_nextsibling(node)) {
369		int cnode;
370		if ((cnode = prom_search(node, name)) != 0)
371			return (cnode);
372	}
373
374	return (0);
375}
376
377/*
378 * Find the named device in the PROM device tree.
379 * XXX - currently we discard any qualifiers attached to device component names
380 */
381int
382obp_v2_finddevice(const char *path)
383{
384	int node;
385	char component[64];
386	char c, *cp;
387	const char *startp, *endp;
388#define IS_SEP(c)	((c) == '/' || (c) == '@' || (c) == ':')
389
390	if (path == NULL)
391		return (-1);
392
393	node = prom_findroot();
394
395	for (startp = path; *startp != '\0'; ) {
396		/*
397		 * Identify next component in path
398		 */
399		while (*startp == '/')
400			startp++;
401
402		endp = startp;
403		while ((c = *endp) != '\0' && !IS_SEP(c))
404			endp++;
405
406		/* Copy component */
407		for (cp = component; startp != endp;) {
408			/* Check component bounds */
409			if (cp > component + sizeof component - 1)
410				return (-1);
411			*cp++ = *startp++;
412		}
413
414		/* Zero terminate this component */
415		*cp = '\0';
416
417		/* Advance `startp' over any non-slash separators */
418		while ((c = *startp) != '\0' && c != '/')
419			startp++;
420
421		node = prom_findnode(prom_firstchild(node), component);
422		if (node == 0)
423			return (-1);
424	}
425
426	return (node);
427}
428
429
430/*
431 * Get the global "options" node Id.
432 */
433int prom_getoptionsnode(void)
434{
435static	int optionsnode;
436
437	if (optionsnode == 0) {
438		optionsnode = prom_findnode(prom_firstchild(prom_findroot()),
439					    "options");
440	}
441	return optionsnode;
442}
443
444/*
445 * Return a property string value from the global "options" node.
446 */
447int prom_getoption(const char *name, char *buf, int buflen)
448{
449	int node = prom_getoptionsnode();
450	int error, len;
451
452	if (buflen == 0)
453		return (EINVAL);
454
455	if (node == 0)
456		return (ENOENT);
457
458	len = buflen - 1;
459	if ((error = prom_getprop(node, name, 1, &len, &buf)) != 0)
460		return error;
461
462	buf[len] = '\0';
463	return (0);
464}
465
466void
467prom_halt(void)
468{
469
470	prom_setcallback(NULL);
471	_prom_halt();
472	panic("PROM exit failed");
473}
474
475void
476prom_boot(char *str)
477{
478
479	prom_setcallback(NULL);
480	_prom_boot(str);
481	panic("PROM boot failed");
482}
483
484
485/*
486 * print debug info to prom.
487 * This is not safe, but then what do you expect?
488 */
489void
490prom_printf(const char *fmt, ...)
491{
492static	char buf[256];
493	int i, len;
494	va_list ap;
495
496	va_start(ap, fmt);
497	len = vsnprintf(buf, sizeof(buf), fmt, ap);
498	va_end(ap);
499
500#if _obp_not_cooked_
501	(*promops.po_write)(promops.po_stdout, buf, len);
502#endif
503
504	for (i = 0; i < len; i++) {
505		int c = buf[i];
506		if (c == '\n')
507			(*promops.po_putchar)('\r');
508		(*promops.po_putchar)(c);
509	}
510}
511
512
513/*
514 * Pass a string to the FORTH PROM to be interpreted.
515 * (Note: may fail silently)
516 */
517static void
518obp_v0_fortheval(const char *s)
519{
520
521	obpvec->pv_fortheval.v0_eval(strlen(s), s);
522}
523
524int
525obp_v0_read(int fd, void *buf, int len)
526{
527	if (fd != prom_stdin())
528		prom_printf("obp_v0_read: unimplemented read from %d\n", fd);
529	return (-1);
530}
531
532int
533obp_v0_write(int fd, const void *buf, int len)
534{
535	if (fd != prom_stdout())
536		prom_printf("obp_v0_write: unimplemented write on %d\n", fd);
537	(*obpvec->pv_putstr)(buf, len);
538	return (-1);
539}
540
541inline void
542obp_v2_putchar(int c)
543{
544	char c0;
545
546	c0 = (c & 0x7f);
547	(*promops.po_write)(promops.po_stdout, &c0, 1);
548}
549
550#if 0
551void
552obp_v2_putchar_cooked(int c)
553{
554
555	if (c == '\n')
556		obp_v2_putchar('\r');
557	obp_v2_putchar(c);
558}
559#endif
560
561int
562obp_v2_getchar(void)
563{
564	char c;
565	int n;
566
567	while ((n = (*promops.po_read)(promops.po_stdin, &c, 1)) != 1)
568		/*void*/;
569	if (c == '\r')
570		c = '\n';
571	return (c);
572}
573
574int
575obp_v2_peekchar(void)
576{
577	char c;
578	int n;
579
580	n = (*promops.po_read)(promops.po_stdin, &c, 1);
581	if (n < 0)
582		return (-1);
583
584	if (c == '\r')
585		c = '\n';
586	return (c);
587}
588
589int
590obp_v2_seek(int handle, u_quad_t offset)
591{
592	uint32_t hi, lo;
593
594	lo = offset & ((uint32_t)-1);
595	hi = (offset >> 32) & ((uint32_t)-1);
596	(*obpvec->pv_v2devops.v2_seek)(handle, hi, lo);
597	return (0);
598}
599
600/*
601 * On SS1s (and also IPCs, SLCs), `promvec->pv_v0bootargs->ba_argv[1]'
602 * contains the flags that were given after the boot command.  On SS2s
603 * (and ELCs, IPXs, etc. and any sun4m class machine), `pv_v0bootargs'
604 * is NULL but `*promvec->pv_v2bootargs.v2_bootargs' points to
605 * "netbsd -s" or whatever.
606 */
607const char *
608obp_v0_getbootpath(void)
609{
610	struct v0bootargs *ba = promops.po_bootcookie;
611	return (ba->ba_argv[0]);
612}
613
614const char *
615obp_v0_getbootargs(void)
616{
617	struct v0bootargs *ba = promops.po_bootcookie;
618	return (ba->ba_argv[1]);
619}
620
621const char *
622obp_v0_getbootfile(void)
623{
624	struct v0bootargs *ba = promops.po_bootcookie;
625	return (ba->ba_kernel);
626}
627
628char *
629parse_bootargs(char *args)
630{
631	char *cp;
632
633	for (cp = args; *cp != '\0'; cp++) {
634		if (*cp == '-') {
635			int c;
636			/*
637			 * Looks like options start here, but check this
638			 * `-' is not part of the kernel name.
639			 */
640			if (cp == args)
641				break;
642			if ((c = *(cp-1)) == ' ' || c == '\t')
643				break;
644		}
645	}
646	return (cp);
647}
648
649const char *
650obp_v2_getbootpath(void)
651{
652	struct v2bootargs *ba = promops.po_bootcookie;
653	return (*ba->v2_bootpath);
654}
655
656const char *
657obp_v2_getbootargs(void)
658{
659	struct v2bootargs *ba = promops.po_bootcookie;
660
661	return (parse_bootargs(*ba->v2_bootargs));
662}
663
664/*
665 * Static storage shared by prom_getbootfile(), prom_getbootargs() and
666 * prom_getbootpath().
667 * Overwritten on each call!
668 */
669static	char storage[128];
670
671char *
672parse_bootfile(char *args)
673{
674	char *cp, *dp;
675
676	cp = args;
677	dp = storage;
678	while (*cp != 0 && *cp != ' ' && *cp != '\t') {
679		if (dp >= storage + sizeof(storage) - 1) {
680			prom_printf("v2_bootargs too long\n");
681			return (NULL);
682		}
683		if (*cp == '-') {
684			int c;
685			/*
686			 * If this `-' is most likely the start of boot
687			 * options, we're done.
688			 */
689			if (cp == args)
690				break;
691			if ((c = *(cp-1)) == ' ' || c == '\t')
692				break;
693		}
694		*dp++ = *cp++;
695	}
696	*dp = '\0';
697	return (storage);
698}
699
700const char *
701obp_v2_getbootfile(void)
702{
703	struct v2bootargs *ba = promops.po_bootcookie;
704	char *kernel = parse_bootfile(*ba->v2_bootargs);
705	char buf[4+1];
706	const char *prop;
707
708	if (kernel[0] != '\0')
709		return kernel;
710
711	/*
712	 * The PROM does not insert the `boot-file' variable if any argument
713	 * was given to the `boot' command (e.g `boot -s'). If we determine
714	 * in parse_bootfile() above, that boot args contain only switches
715	 * then get the `boot-file' value (if any) ourselves.
716	 * If the `diag-switch?' PROM variable is set to true, we use
717	 * `diag-file' instead.
718	 */
719	prop = (prom_getoption("diag-switch?", buf, sizeof buf) != 0 ||
720		strcmp(buf, "true") != 0)
721		? "diag-file"
722		: "boot-file";
723
724	if (prom_getoption(prop, storage, sizeof storage) != 0)
725		return (NULL);
726
727	return (storage);
728}
729
730void
731obp_v2_putstr(const char *str, int len)
732{
733	prom_write(prom_stdout(), str, len);
734}
735
736void
737obp_set_callback(void (*f)(void))
738{
739	*obpvec->pv_synchook = f;
740}
741
742int
743obp_ticks(void)
744{
745
746	return (*((int *)promops.po_tickdata));
747}
748
749static int
750findchosen(void)
751{
752static	int chosennode;
753	int node;
754
755	if ((node = chosennode) == 0 && (node = OF_finddevice("/chosen")) == -1)
756		panic("no CHOSEN node");
757
758	chosennode = node;
759	return (node);
760}
761
762static int
763opf_finddevice(const char *name)
764{
765	int phandle = OF_finddevice(name);
766	if (phandle == -1)
767		return (0);
768	else
769		return (phandle);
770}
771
772static int
773opf_instance_to_package(int ihandle)
774{
775	int phandle = OF_instance_to_package(ihandle);
776	if (phandle == -1)
777		return (0);
778	else
779		return (phandle);
780}
781
782
783static const char *
784opf_getbootpath(void)
785{
786	int node = findchosen();
787	char *buf = storage;
788	int blen = sizeof storage;
789
790	if (prom_getprop(node, "bootpath", 1, &blen, &buf) != 0)
791		return ("");
792
793	return (buf);
794}
795
796static const char *
797opf_getbootargs(void)
798{
799	int node = findchosen();
800	char *buf = storage;
801	int blen = sizeof storage;
802
803	if (prom_getprop(node, "bootargs", 1, &blen, &buf) != 0)
804		return ("");
805
806	return (parse_bootargs(buf));
807}
808
809static const char *
810opf_getbootfile(void)
811{
812	int node = findchosen();
813	char *buf = storage;
814	int blen = sizeof storage;
815
816	if (prom_getprop(node, "bootargs", 1, &blen, &buf) != 0)
817		return ("");
818
819	return (parse_bootfile(buf));
820}
821
822static char *
823opf_nextprop(int node, const char *prop)
824{
825#define OF_NEXTPROP_BUF_SIZE 32	/* specified by the standard */
826	static char buf[OF_NEXTPROP_BUF_SIZE];
827	OF_nextprop(node, prop, buf);
828	return (buf);
829}
830
831void
832opf_interpret_simple(const char *s)
833{
834	(void)OF_interpret(s, 0, 0);
835}
836
837/*
838 * Retrieve physical memory information from the PROM.
839 * If ap is NULL, return the required length of the array.
840 */
841int
842prom_makememarr(struct memarr *ap, int xmax, int which)
843{
844	struct v0mlist *mp;
845	int node, n;
846	const char *prop;
847
848	if (which != MEMARR_AVAILPHYS && which != MEMARR_TOTALPHYS)
849		panic("makememarr");
850
851	/*
852	 * `struct memarr' is in V2 memory property format.
853	 * On previous ROM versions we must convert.
854	 */
855	switch (prom_version()) {
856		struct promvec *promvec;
857		struct om_vector *oldpvec;
858	case PROM_OLDMON:
859		oldpvec = (struct om_vector *)PROM_BASE;
860		n = 1;
861		if (ap != NULL) {
862			ap[0].zero = 0;
863			ap[0].addr = 0;
864			ap[0].len = (which == MEMARR_AVAILPHYS)
865				? *oldpvec->memoryAvail
866				: *oldpvec->memorySize;
867		}
868		break;
869
870	case PROM_OBP_V0:
871		/*
872		 * Version 0 PROMs use a linked list to describe these
873		 * guys.
874		 */
875		promvec = romp;
876		mp = (which == MEMARR_AVAILPHYS)
877			? *promvec->pv_v0mem.v0_physavail
878			: *promvec->pv_v0mem.v0_phystot;
879		for (n = 0; mp != NULL; mp = mp->next, n++) {
880			if (ap == NULL)
881				continue;
882			if (n >= xmax) {
883				printf("makememarr: WARNING: lost some memory\n");
884				break;
885			}
886			ap->zero = 0;
887			ap->addr = (u_long)mp->addr;
888			ap->len = mp->nbytes;
889			ap++;
890		}
891		break;
892
893	default:
894		printf("makememarr: hope version %d PROM is like version 2\n",
895			prom_version());
896		/* FALLTHROUGH */
897
898        case PROM_OBP_V3:
899	case PROM_OBP_V2:
900		/*
901		 * Version 2 PROMs use a property array to describe them.
902		 */
903
904		/* Consider emulating `OF_finddevice' */
905		node = findnode(firstchild(findroot()), "memory");
906		goto case_common;
907
908	case PROM_OPENFIRM:
909		node = OF_finddevice("/memory");
910		if (node == -1)
911			node = 0;
912
913	case_common:
914		if (node == 0)
915			panic("makememarr: cannot find \"memory\" node");
916
917		prop = (which == MEMARR_AVAILPHYS) ? "available" : "reg";
918		if (ap == NULL) {
919			n = prom_getproplen(node, prop);
920		} else {
921			n = xmax;
922			if (prom_getprop(node, prop, sizeof(struct memarr),
923					&n, &ap) != 0)
924				panic("makememarr: cannot get property");
925		}
926		break;
927	}
928
929	if (n <= 0)
930		panic("makememarr: no memory found");
931	/*
932	 * Success!  (Hooray)
933	 */
934	return (n);
935}
936
937static struct idprom idprom;
938#ifdef _STANDALONE
939long hostid;
940#endif
941
942struct idprom *
943prom_getidprom(void)
944{
945	int node, len;
946	u_long h;
947	u_char *dst;
948
949	if (idprom.idp_format != 0)
950		/* Already got it */
951		return (&idprom);
952
953	dst = (u_char *)&idprom;
954	len = sizeof(struct idprom);
955
956	switch (prom_version()) {
957	case PROM_OLDMON:
958#ifdef AC_IDPROM
959		{
960			u_char *src = (u_char *)AC_IDPROM;
961			do {
962				*dst++ = lduba(src++, ASI_CONTROL);
963			} while (--len > 0);
964		}
965#endif
966		break;
967
968	/*
969	 * Fetch the `idprom' property at the root node.
970	 */
971	case PROM_OBP_V0:
972	case PROM_OBP_V2:
973	case PROM_OPENFIRM:
974	case PROM_OBP_V3:
975		node = prom_findroot();
976		if (prom_getprop(node, "idprom", 1, &len, &dst) != 0) {
977			printf("`idprom' property cannot be read: "
978				"cannot get ethernet address");
979		}
980		break;
981	}
982
983	/* Establish hostid */
984	h =  (u_int)idprom.idp_machtype << 24;
985	h |= idprom.idp_serialnum[0] << 16;
986	h |= idprom.idp_serialnum[1] << 8;
987	h |= idprom.idp_serialnum[2];
988	hostid = h;
989
990	return (&idprom);
991}
992
993void prom_getether(int node, u_char *cp)
994{
995	struct idprom *idp;
996
997	if (prom_get_node_ether(node, cp))
998		return;
999
1000	/* Fall back on the machine's global ethernet address */
1001	idp = prom_getidprom();
1002	memcpy(cp, idp->idp_etheraddr, 6);
1003}
1004
1005bool
1006prom_get_node_ether(int node, u_char *cp)
1007{
1008	char buf[6+1], *bp;
1009	int nitem;
1010
1011	if (node == 0)
1012		return false;
1013
1014	/*
1015	 * First, try the node's "mac-address" property.
1016	 * This property is set by the adapter's firmware if the
1017	 * device has already been opened for traffic, e.g. for
1018	 * net booting.  Its value might be `0-terminated', probably
1019	 * because the Forth ROMs uses `xdrstring' instead of `xdrbytes'
1020	 * to construct the property.
1021	 */
1022	nitem = 6+1;
1023	bp = buf;
1024	if (prom_getprop(node, "mac-address", 1, &nitem, &bp) == 0 &&
1025	    nitem >= 6) {
1026		memcpy(cp, bp, 6);
1027		return true;
1028	}
1029
1030	/*
1031	 * Next, check the global "local-mac-address?" switch to see
1032	 * if we should try to extract the node's "local-mac-address"
1033	 * property.
1034	 */
1035	if (prom_getoption("local-mac-address?", buf, sizeof buf) != 0 ||
1036	    strcmp(buf, "true") != 0)
1037		return false;
1038
1039	/* Retrieve the node's "local-mac-address" property, if any */
1040	nitem = 6;
1041	if (prom_getprop(node, "local-mac-address", 1, &nitem, &cp) == 0 &&
1042	    nitem == 6)
1043		return true;
1044
1045	return false;
1046}
1047
1048/*
1049 * The integer property "get-unum" on the root device is the address
1050 * of a callable function in the PROM that takes a physical address
1051 * (in lo/hipart format) and returns a string identifying the chip
1052 * location of the corresponding memory cell.
1053 */
1054const char *
1055prom_pa_location(u_int phys_lo, u_int phys_hi)
1056{
1057	static char *(*unum)(u_int, u_int);
1058	char *str;
1059	const char *unk = "<Unknown>";
1060
1061	switch (prom_version()) {
1062	case PROM_OLDMON:
1063	case PROM_OPENFIRM:
1064		/* to do */
1065	default:
1066		break;
1067	case PROM_OBP_V0:
1068	case PROM_OBP_V2:
1069	case PROM_OBP_V3:
1070		if (unum == NULL)
1071			unum = (char *(*)(u_int,u_int))(u_long)
1072				prom_getpropint(prom_findroot(), "get-unum", 0);
1073
1074		if (unum == NULL || (str = unum(phys_lo, phys_hi)) == NULL)
1075			break;
1076
1077		return (str);
1078	}
1079
1080	return (unk);
1081}
1082
1083static void prom_init_oldmon(void);
1084static void prom_init_obp(void);
1085static void prom_init_opf(void);
1086
1087static inline void
1088prom_init_oldmon(void)
1089{
1090	struct om_vector *oldpvec = (struct om_vector *)PROM_BASE;
1091	extern void sparc_noop(void);
1092
1093	promops.po_version = PROM_OLDMON;
1094	promops.po_revision = oldpvec->monId[0];	/*XXX*/
1095
1096	promops.po_stdin = *oldpvec->inSource;
1097	promops.po_stdout = *oldpvec->outSink;
1098
1099	promops.po_bootcookie = *oldpvec->bootParam; /* deref 1 lvl */
1100	promops.po_bootpath = obp_v0_getbootpath;
1101	promops.po_bootfile = obp_v0_getbootfile;
1102	promops.po_bootargs = obp_v0_getbootargs;
1103
1104	promops.po_putchar = oldpvec->putChar;
1105	promops.po_getchar = oldpvec->getChar;
1106	promops.po_peekchar = oldpvec->mayGet;
1107	promops.po_putstr = oldpvec->fbWriteStr;
1108	promops.po_reboot = oldpvec->reBoot;
1109	promops.po_abort = oldpvec->abortEntry;
1110	promops.po_halt = oldpvec->exitToMon;
1111	promops.po_ticks = obp_ticks;
1112	promops.po_tickdata = oldpvec->nmiClock;
1113	promops.po_setcallback = (void *)sparc_noop;
1114	promops.po_setcontext = oldpvec->setcxsegmap;
1115
1116#ifdef SUN4
1117#ifndef _STANDALONE
1118	if (oldpvec->romvecVersion >= 2) {
1119		extern void oldmon_w_cmd(u_long, char *);
1120		*oldpvec->vector_cmd = oldmon_w_cmd;
1121	}
1122#endif
1123#endif
1124}
1125
1126static inline void
1127prom_init_obp(void)
1128{
1129	struct nodeops *no;
1130
1131	/*
1132	 * OBP v0, v2 & v3
1133	 */
1134	switch (obpvec->pv_romvec_vers) {
1135	case 0:
1136		promops.po_version = PROM_OBP_V0;
1137		break;
1138	case 2:
1139		promops.po_version = PROM_OBP_V2;
1140		break;
1141	case 3:
1142		promops.po_version = PROM_OBP_V3;
1143		break;
1144	default:
1145		obpvec->pv_halt();	/* What else? */
1146	}
1147
1148	promops.po_revision = obpvec->pv_printrev;
1149
1150	promops.po_halt = obpvec->pv_halt;
1151	promops.po_reboot = obpvec->pv_reboot;
1152	promops.po_abort = obpvec->pv_abort;
1153	promops.po_setcontext = obpvec->pv_setctxt;
1154	promops.po_setcallback = obp_set_callback;
1155	promops.po_ticks = obp_ticks;
1156	promops.po_tickdata = obpvec->pv_ticks;
1157
1158	/*
1159	 * Remove indirection through `pv_nodeops' while we're here.
1160	 * Hopefully, the PROM has no need to change this pointer on the fly..
1161	 */
1162	no = obpvec->pv_nodeops;
1163	promops.po_firstchild = no->no_child;
1164	promops.po_nextsibling = no->no_nextnode;
1165	promops.po_getproplen = no->no_proplen;
1166	/* XXX - silently discard getprop's `len' argument */
1167	promops.po_getprop = (void *)no->no_getprop;
1168	promops.po_setprop = no->no_setprop;
1169	promops.po_nextprop = no->no_nextprop;
1170
1171	/*
1172	 * Next, deal with prom vector differences between versions.
1173	 */
1174	switch (promops.po_version) {
1175	case PROM_OBP_V0:
1176		promops.po_stdin = *obpvec->pv_stdin;
1177		promops.po_stdout = *obpvec->pv_stdout;
1178		promops.po_bootcookie = *obpvec->pv_v0bootargs; /* deref 1 lvl */
1179		promops.po_bootpath = obp_v0_getbootpath;
1180		promops.po_bootfile = obp_v0_getbootfile;
1181		promops.po_bootargs = obp_v0_getbootargs;
1182		promops.po_putchar = obpvec->pv_putchar;
1183		promops.po_getchar = obpvec->pv_getchar;
1184		promops.po_peekchar = obpvec->pv_nbgetchar;
1185		promops.po_putstr = obpvec->pv_putstr;
1186		promops.po_open = obpvec->pv_v0devops.v0_open;
1187		promops.po_close = (void *)obpvec->pv_v0devops.v0_close;
1188		promops.po_read = obp_v0_read;
1189		promops.po_write = obp_v0_write;
1190		promops.po_interpret = obp_v0_fortheval;
1191		break;
1192	case PROM_OBP_V3:
1193		promops.po_cpustart = obpvec->pv_v3cpustart;
1194		promops.po_cpustop = obpvec->pv_v3cpustop;
1195		promops.po_cpuidle = obpvec->pv_v3cpuidle;
1196		promops.po_cpuresume = obpvec->pv_v3cpuresume;
1197		/*FALLTHROUGH*/
1198	case PROM_OBP_V2:
1199		/* Deref stdio handles one level */
1200		promops.po_stdin = *obpvec->pv_v2bootargs.v2_fd0;
1201		promops.po_stdout = *obpvec->pv_v2bootargs.v2_fd1;
1202
1203		promops.po_bootcookie = &obpvec->pv_v2bootargs;
1204		promops.po_bootpath = obp_v2_getbootpath;
1205		promops.po_bootfile = obp_v2_getbootfile;
1206		promops.po_bootargs = obp_v2_getbootargs;
1207
1208		promops.po_interpret = obpvec->pv_fortheval.v2_eval;
1209
1210		promops.po_putchar = obp_v2_putchar;
1211		promops.po_getchar = obp_v2_getchar;
1212		promops.po_peekchar = obp_v2_peekchar;
1213		promops.po_putstr = obp_v2_putstr;
1214		promops.po_open = obpvec->pv_v2devops.v2_open;
1215		promops.po_close = (void *)obpvec->pv_v2devops.v2_close;
1216		promops.po_read = obpvec->pv_v2devops.v2_read;
1217		promops.po_write = obpvec->pv_v2devops.v2_write;
1218		promops.po_seek = obp_v2_seek;
1219		promops.po_instance_to_package = obpvec->pv_v2devops.v2_fd_phandle;
1220		promops.po_finddevice = obp_v2_finddevice;
1221
1222#ifndef _STANDALONE
1223		prom_printf("OBP version %d, revision %d.%d (plugin rev %x)\n",
1224			obpvec->pv_romvec_vers,
1225			obpvec->pv_printrev >> 16, obpvec->pv_printrev & 0xffff,
1226			obpvec->pv_plugin_vers);
1227#endif
1228		break;
1229	}
1230}
1231
1232static inline void
1233prom_init_opf(void)
1234{
1235	int node;
1236
1237	promops.po_version = PROM_OPENFIRM;
1238
1239	/*
1240	 * OpenFirmware ops are mostly straightforward.
1241	 */
1242	promops.po_halt = OF_exit;
1243	promops.po_reboot = OF_boot;
1244	promops.po_abort = OF_enter;
1245	promops.po_interpret = opf_interpret_simple;
1246	promops.po_setcallback = (void *)OF_set_callback;
1247	promops.po_ticks = OF_milliseconds;
1248
1249	promops.po_bootpath = opf_getbootpath;
1250	promops.po_bootfile = opf_getbootfile;
1251	promops.po_bootargs = opf_getbootargs;
1252
1253	promops.po_firstchild = OF_child;
1254	promops.po_nextsibling = OF_peer;
1255	promops.po_getproplen = OF_getproplen;
1256	promops.po_getprop = OF_getprop;
1257	promops.po_nextprop = opf_nextprop;
1258	promops.po_setprop = OF_setprop;
1259
1260	/* We can re-use OBP v2 emulation */
1261	promops.po_putchar = obp_v2_putchar;
1262	promops.po_getchar = obp_v2_getchar;
1263	promops.po_peekchar = obp_v2_peekchar;
1264	promops.po_putstr = obp_v2_putstr;
1265
1266	promops.po_open = OF_open;
1267	promops.po_close = OF_close;
1268	promops.po_read = OF_read;
1269	promops.po_write = OF_write;
1270	promops.po_seek = OF_seek;
1271	promops.po_instance_to_package = opf_instance_to_package;
1272	promops.po_finddevice = opf_finddevice;
1273
1274	/* Retrieve and cache stdio handles */
1275	node = findchosen();
1276	OF_getprop(node, "stdin", &promops.po_stdin, sizeof(int));
1277	OF_getprop(node, "stdout", &promops.po_stdout, sizeof(int));
1278}
1279
1280/*
1281 * Initialize our PROM operations vector.
1282 */
1283void
1284prom_init(void)
1285{
1286#ifdef _STANDALONE
1287	int node;
1288	char *cp;
1289#endif
1290
1291	if (CPU_ISSUN4) {
1292		prom_init_oldmon();
1293	} else if (obpvec->pv_magic == OBP_MAGIC) {
1294		prom_init_obp();
1295	} else {
1296		/*
1297		 * Assume this is an Openfirm machine.
1298		 */
1299		prom_init_opf();
1300	}
1301
1302#ifdef _STANDALONE
1303	/*
1304	 * Find out what type of machine we're running on.
1305	 *
1306	 * This process is actually started in srt0.S, which has discovered
1307	 * the minimal set of machine specific parameters for the 1st-level
1308	 * boot program (bootxx) to run. The page size has already been set
1309	 * and the CPU type is either CPU_SUN4, CPU_SUN4C or CPU_SUN4M.
1310	 */
1311
1312	if (cputyp == CPU_SUN4 || cputyp == CPU_SUN4M)
1313		return;
1314
1315	/*
1316	 * We have SUN4C, SUN4M or SUN4D.
1317	 * Use the PROM `compatible' property to determine which.
1318	 * Absence of the `compatible' property means `sun4c'.
1319	 */
1320
1321	node = prom_findroot();
1322	cp = prom_getpropstring(node, "compatible");
1323	if (*cp == '\0' || strcmp(cp, "sun4c") == 0)
1324		cputyp = CPU_SUN4C;
1325	else if (strcmp(cp, "sun4m") == 0)
1326		cputyp = CPU_SUN4M;
1327	else if (strcmp(cp, "sun4d") == 0)
1328		cputyp = CPU_SUN4D;
1329	else
1330		printf("Unknown CPU type (compatible=`%s')\n", cp);
1331#endif /* _STANDALONE */
1332}
1333