openfirm.c revision 271190
1/*	$NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $	*/
2
3/*-
4 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5 * Copyright (C) 1995, 1996 TooLs GmbH.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by TooLs GmbH.
19 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33/*-
34 * Copyright (C) 2000 Benno Rice.
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 *
46 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
49 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
52 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
54 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
55 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57
58#include <sys/cdefs.h>
59__FBSDID("$FreeBSD: head/sys/dev/ofw/openfirm.c 271190 2014-09-06 15:11:35Z ian $");
60
61#include "opt_platform.h"
62
63#include <sys/param.h>
64#include <sys/kernel.h>
65#include <sys/malloc.h>
66#include <sys/systm.h>
67#include <sys/endian.h>
68
69#include <machine/stdarg.h>
70
71#include <dev/ofw/ofwvar.h>
72#include <dev/ofw/openfirm.h>
73
74#include "ofw_if.h"
75
76static void OF_putchar(int c, void *arg);
77
78MALLOC_DEFINE(M_OFWPROP, "openfirm", "Open Firmware properties");
79
80static ihandle_t stdout;
81
82static ofw_def_t	*ofw_def_impl = NULL;
83static ofw_t		ofw_obj;
84static struct ofw_kobj	ofw_kernel_obj;
85static struct kobj_ops	ofw_kernel_kops;
86
87struct xrefinfo {
88	phandle_t	xref;
89	phandle_t 	node;
90	device_t  	dev;
91	SLIST_ENTRY(xrefinfo) next_entry;
92};
93
94static SLIST_HEAD(, xrefinfo) xreflist = SLIST_HEAD_INITIALIZER(xreflist);
95static boolean_t xref_init_done;
96
97#define	FIND_BY_XREF	0
98#define	FIND_BY_NODE	1
99#define	FIND_BY_DEV	2
100
101/*
102 * xref-phandle-device lookup helper routines.
103 *
104 * As soon as we are able to use malloc(), walk the node tree and build a list
105 * of info that cross-references node handles, xref handles, and device_t
106 * instances.  This list exists primarily to allow association of a device_t
107 * with an xref handle, but it is also used to speed up translation between xref
108 * and node handles.  Before malloc() is available we have to recursively search
109 * the node tree each time we want to translate between a node and xref handle.
110 * Afterwards we can do the translations by searching this much shorter list.
111 */
112static void
113xrefinfo_create(phandle_t node)
114{
115	struct xrefinfo * xi;
116	phandle_t child, xref;
117
118	/*
119	 * Recursively descend from parent, looking for nodes with a property
120	 * named either "phandle", "ibm,phandle", or "linux,phandle".  For each
121	 * such node found create an entry in the xreflist.
122	 */
123	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
124		xrefinfo_create(child);
125		if (OF_getencprop(child, "phandle", &xref, sizeof(xref)) ==
126		    -1 && OF_getencprop(child, "ibm,phandle", &xref,
127		    sizeof(xref)) == -1 && OF_getencprop(child,
128		    "linux,phandle", &xref, sizeof(xref)) == -1)
129			continue;
130		xi = malloc(sizeof(*xi), M_OFWPROP, M_WAITOK | M_ZERO);
131		xi->node = child;
132		xi->xref = xref;
133		SLIST_INSERT_HEAD(&xreflist, xi, next_entry);
134	}
135}
136
137static void
138xrefinfo_init(void *unsed)
139{
140
141	xrefinfo_create(OF_peer(0));
142	xref_init_done = true;
143}
144SYSINIT(xrefinfo, SI_SUB_KMEM, SI_ORDER_ANY, xrefinfo_init, NULL);
145
146static struct xrefinfo *
147xrefinfo_find(phandle_t phandle, int find_by)
148{
149	struct xrefinfo * xi;
150
151	SLIST_FOREACH(xi, &xreflist, next_entry) {
152		if (find_by == FIND_BY_XREF && phandle == xi->xref)
153			return (xi);
154		else if (find_by == FIND_BY_NODE && phandle == xi->node)
155			return (xi);
156		else if (find_by == FIND_BY_DEV && phandle == (uintptr_t)xi->dev)
157			return (xi);
158	}
159	return (NULL);
160}
161
162/*
163 * OFW install routines.  Highest priority wins, equal priority also
164 * overrides allowing last-set to win.
165 */
166SET_DECLARE(ofw_set, ofw_def_t);
167
168boolean_t
169OF_install(char *name, int prio)
170{
171	ofw_def_t *ofwp, **ofwpp;
172	static int curr_prio = 0;
173
174	/*
175	 * Try and locate the OFW kobj corresponding to the name.
176	 */
177	SET_FOREACH(ofwpp, ofw_set) {
178		ofwp = *ofwpp;
179
180		if (ofwp->name &&
181		    !strcmp(ofwp->name, name) &&
182		    prio >= curr_prio) {
183			curr_prio = prio;
184			ofw_def_impl = ofwp;
185			return (TRUE);
186		}
187	}
188
189	return (FALSE);
190}
191
192/* Initializer */
193int
194OF_init(void *cookie)
195{
196	phandle_t chosen;
197	int rv;
198
199	if (ofw_def_impl == NULL)
200		return (-1);
201
202	ofw_obj = &ofw_kernel_obj;
203	/*
204	 * Take care of compiling the selected class, and
205	 * then statically initialize the OFW object.
206	 */
207	kobj_class_compile_static(ofw_def_impl, &ofw_kernel_kops);
208	kobj_init_static((kobj_t)ofw_obj, ofw_def_impl);
209
210	rv = OFW_INIT(ofw_obj, cookie);
211
212	if ((chosen = OF_finddevice("/chosen")) != -1)
213		if (OF_getencprop(chosen, "stdout", &stdout,
214		    sizeof(stdout)) == -1)
215			stdout = -1;
216
217	return (rv);
218}
219
220static void
221OF_putchar(int c, void *arg __unused)
222{
223	char cbuf;
224
225	if (c == '\n') {
226		cbuf = '\r';
227		OF_write(stdout, &cbuf, 1);
228	}
229
230	cbuf = c;
231	OF_write(stdout, &cbuf, 1);
232}
233
234void
235OF_printf(const char *fmt, ...)
236{
237	va_list	va;
238
239	va_start(va, fmt);
240	(void)kvprintf(fmt, OF_putchar, NULL, 10, va);
241	va_end(va);
242}
243
244/*
245 * Generic functions
246 */
247
248/* Test to see if a service exists. */
249int
250OF_test(const char *name)
251{
252
253	if (ofw_def_impl == NULL)
254		return (-1);
255
256	return (OFW_TEST(ofw_obj, name));
257}
258
259int
260OF_interpret(const char *cmd, int nreturns, ...)
261{
262	va_list ap;
263	cell_t slots[16];
264	int i = 0;
265	int status;
266
267	if (ofw_def_impl == NULL)
268		return (-1);
269
270	status = OFW_INTERPRET(ofw_obj, cmd, nreturns, slots);
271	if (status == -1)
272		return (status);
273
274	va_start(ap, nreturns);
275	while (i < nreturns)
276		*va_arg(ap, cell_t *) = slots[i++];
277	va_end(ap);
278
279	return (status);
280}
281
282/*
283 * Device tree functions
284 */
285
286/* Return the next sibling of this node or 0. */
287phandle_t
288OF_peer(phandle_t node)
289{
290
291	if (ofw_def_impl == NULL)
292		return (0);
293
294	return (OFW_PEER(ofw_obj, node));
295}
296
297/* Return the first child of this node or 0. */
298phandle_t
299OF_child(phandle_t node)
300{
301
302	if (ofw_def_impl == NULL)
303		return (0);
304
305	return (OFW_CHILD(ofw_obj, node));
306}
307
308/* Return the parent of this node or 0. */
309phandle_t
310OF_parent(phandle_t node)
311{
312
313	if (ofw_def_impl == NULL)
314		return (0);
315
316	return (OFW_PARENT(ofw_obj, node));
317}
318
319/* Return the package handle that corresponds to an instance handle. */
320phandle_t
321OF_instance_to_package(ihandle_t instance)
322{
323
324	if (ofw_def_impl == NULL)
325		return (-1);
326
327	return (OFW_INSTANCE_TO_PACKAGE(ofw_obj, instance));
328}
329
330/* Get the length of a property of a package. */
331ssize_t
332OF_getproplen(phandle_t package, const char *propname)
333{
334
335	if (ofw_def_impl == NULL)
336		return (-1);
337
338	return (OFW_GETPROPLEN(ofw_obj, package, propname));
339}
340
341/* Check existence of a property of a package. */
342int
343OF_hasprop(phandle_t package, const char *propname)
344{
345
346	return (OF_getproplen(package, propname) >= 0 ? 1 : 0);
347}
348
349/* Get the value of a property of a package. */
350ssize_t
351OF_getprop(phandle_t package, const char *propname, void *buf, size_t buflen)
352{
353
354	if (ofw_def_impl == NULL)
355		return (-1);
356
357	return (OFW_GETPROP(ofw_obj, package, propname, buf, buflen));
358}
359
360ssize_t
361OF_getencprop(phandle_t node, const char *propname, pcell_t *buf, size_t len)
362{
363	ssize_t retval;
364	int i;
365
366	KASSERT(len % 4 == 0, ("Need a multiple of 4 bytes"));
367
368	retval = OF_getprop(node, propname, buf, len);
369	for (i = 0; i < len/4; i++)
370		buf[i] = be32toh(buf[i]);
371
372	return (retval);
373}
374
375/*
376 * Recursively search the node and its parent for the given property, working
377 * downward from the node to the device tree root.  Returns the value of the
378 * first match.
379 */
380ssize_t
381OF_searchprop(phandle_t node, const char *propname, void *buf, size_t len)
382{
383	ssize_t rv;
384
385	for (; node != 0; node = OF_parent(node))
386		if ((rv = OF_getprop(node, propname, buf, len)) != -1)
387			return (rv);
388	return (-1);
389}
390
391ssize_t
392OF_searchencprop(phandle_t node, const char *propname, void *buf, size_t len)
393{
394	ssize_t rv;
395
396	for (; node != 0; node = OF_parent(node))
397		if ((rv = OF_getencprop(node, propname, buf, len)) != -1)
398			return (rv);
399	return (-1);
400}
401
402/*
403 * Store the value of a property of a package into newly allocated memory
404 * (using the M_OFWPROP malloc pool and M_WAITOK).  elsz is the size of a
405 * single element, the number of elements is return in number.
406 */
407ssize_t
408OF_getprop_alloc(phandle_t package, const char *propname, int elsz, void **buf)
409{
410	int len;
411
412	*buf = NULL;
413	if ((len = OF_getproplen(package, propname)) == -1 ||
414	    len % elsz != 0)
415		return (-1);
416
417	*buf = malloc(len, M_OFWPROP, M_WAITOK);
418	if (OF_getprop(package, propname, *buf, len) == -1) {
419		free(*buf, M_OFWPROP);
420		*buf = NULL;
421		return (-1);
422	}
423	return (len / elsz);
424}
425
426ssize_t
427OF_getencprop_alloc(phandle_t package, const char *name, int elsz, void **buf)
428{
429	ssize_t retval;
430	pcell_t *cell;
431	int i;
432
433	retval = OF_getprop_alloc(package, name, elsz, buf);
434	if (retval == -1 || retval*elsz % 4 != 0)
435		return (-1);
436
437	cell = *buf;
438	for (i = 0; i < retval*elsz/4; i++)
439		cell[i] = be32toh(cell[i]);
440
441	return (retval);
442}
443
444/* Get the next property of a package. */
445int
446OF_nextprop(phandle_t package, const char *previous, char *buf, size_t size)
447{
448
449	if (ofw_def_impl == NULL)
450		return (-1);
451
452	return (OFW_NEXTPROP(ofw_obj, package, previous, buf, size));
453}
454
455/* Set the value of a property of a package. */
456int
457OF_setprop(phandle_t package, const char *propname, const void *buf, size_t len)
458{
459
460	if (ofw_def_impl == NULL)
461		return (-1);
462
463	return (OFW_SETPROP(ofw_obj, package, propname, buf,len));
464}
465
466/* Convert a device specifier to a fully qualified pathname. */
467ssize_t
468OF_canon(const char *device, char *buf, size_t len)
469{
470
471	if (ofw_def_impl == NULL)
472		return (-1);
473
474	return (OFW_CANON(ofw_obj, device, buf, len));
475}
476
477/* Return a package handle for the specified device. */
478phandle_t
479OF_finddevice(const char *device)
480{
481
482	if (ofw_def_impl == NULL)
483		return (-1);
484
485	return (OFW_FINDDEVICE(ofw_obj, device));
486}
487
488/* Return the fully qualified pathname corresponding to an instance. */
489ssize_t
490OF_instance_to_path(ihandle_t instance, char *buf, size_t len)
491{
492
493	if (ofw_def_impl == NULL)
494		return (-1);
495
496	return (OFW_INSTANCE_TO_PATH(ofw_obj, instance, buf, len));
497}
498
499/* Return the fully qualified pathname corresponding to a package. */
500ssize_t
501OF_package_to_path(phandle_t package, char *buf, size_t len)
502{
503
504	if (ofw_def_impl == NULL)
505		return (-1);
506
507	return (OFW_PACKAGE_TO_PATH(ofw_obj, package, buf, len));
508}
509
510/* Look up effective phandle (see FDT/PAPR spec) */
511static phandle_t
512OF_child_xref_phandle(phandle_t parent, phandle_t xref)
513{
514	phandle_t child, rxref;
515
516	/*
517	 * Recursively descend from parent, looking for a node with a property
518	 * named either "phandle", "ibm,phandle", or "linux,phandle" that
519	 * matches the xref we are looking for.
520	 */
521
522	for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
523		rxref = OF_child_xref_phandle(child, xref);
524		if (rxref != -1)
525			return (rxref);
526
527		if (OF_getencprop(child, "phandle", &rxref, sizeof(rxref)) ==
528		    -1 && OF_getencprop(child, "ibm,phandle", &rxref,
529		    sizeof(rxref)) == -1 && OF_getencprop(child,
530		    "linux,phandle", &rxref, sizeof(rxref)) == -1)
531			continue;
532
533		if (rxref == xref)
534			return (child);
535	}
536
537	return (-1);
538}
539
540phandle_t
541OF_node_from_xref(phandle_t xref)
542{
543	struct xrefinfo *xi;
544	phandle_t node;
545
546	if (xref_init_done) {
547		if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL)
548			return (xref);
549		return (xi->node);
550	}
551
552	if ((node = OF_child_xref_phandle(OF_peer(0), xref)) == -1)
553		return (xref);
554	return (node);
555}
556
557static phandle_t
558xref_from_node(phandle_t node, phandle_t notfoundvalue)
559{
560	struct xrefinfo *xi;
561	phandle_t xref;
562
563	if (xref_init_done) {
564		if ((xi = xrefinfo_find(node, FIND_BY_NODE)) == NULL)
565			return (notfoundvalue);
566		return (xi->xref);
567	}
568
569	if (OF_getencprop(node, "phandle", &xref, sizeof(xref)) ==
570	    -1 && OF_getencprop(node, "ibm,phandle", &xref,
571	    sizeof(xref)) == -1 && OF_getencprop(node,
572	    "linux,phandle", &xref, sizeof(xref)) == -1)
573		return (notfoundvalue);
574	return (xref);
575}
576
577phandle_t
578OF_xref_from_node(phandle_t node)
579{
580
581	return (xref_from_node(node, node));
582}
583
584phandle_t
585OF_xref_from_node_strict(phandle_t node)
586{
587
588	return (xref_from_node(node, -1));
589}
590
591device_t
592OF_device_from_xref(phandle_t xref)
593{
594	struct xrefinfo *xi;
595
596	if (xref_init_done) {
597		if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL)
598			return (NULL);
599		return (xi->dev);
600	}
601	panic("Attempt to find device before xreflist_init");
602}
603
604phandle_t
605OF_xref_from_device(device_t dev)
606{
607	struct xrefinfo *xi;
608
609	if (xref_init_done) {
610		if ((xi = xrefinfo_find((uintptr_t)dev, FIND_BY_DEV)) == NULL)
611			return (0);
612		return (xi->xref);
613	}
614	panic("Attempt to find xref before xreflist_init");
615}
616
617int
618OF_device_register_xref(phandle_t xref, device_t dev)
619{
620	struct xrefinfo *xi;
621
622	if (xref_init_done) {
623		if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL)
624			return (ENXIO);
625		xi->dev = dev;
626		return (0);
627	}
628	panic("Attempt to register device before xreflist_init");
629}
630
631/*  Call the method in the scope of a given instance. */
632int
633OF_call_method(const char *method, ihandle_t instance, int nargs, int nreturns,
634    ...)
635{
636	va_list ap;
637	cell_t args_n_results[12];
638	int n, status;
639
640	if (nargs > 6 || ofw_def_impl == NULL)
641		return (-1);
642	va_start(ap, nreturns);
643	for (n = 0; n < nargs; n++)
644		args_n_results[n] = va_arg(ap, cell_t);
645
646	status = OFW_CALL_METHOD(ofw_obj, instance, method, nargs, nreturns,
647	    args_n_results);
648	if (status != 0)
649		return (status);
650
651	for (; n < nargs + nreturns; n++)
652		*va_arg(ap, cell_t *) = args_n_results[n];
653	va_end(ap);
654	return (0);
655}
656
657/*
658 * Device I/O functions
659 */
660
661/* Open an instance for a device. */
662ihandle_t
663OF_open(const char *device)
664{
665
666	if (ofw_def_impl == NULL)
667		return (0);
668
669	return (OFW_OPEN(ofw_obj, device));
670}
671
672/* Close an instance. */
673void
674OF_close(ihandle_t instance)
675{
676
677	if (ofw_def_impl == NULL)
678		return;
679
680	OFW_CLOSE(ofw_obj, instance);
681}
682
683/* Read from an instance. */
684ssize_t
685OF_read(ihandle_t instance, void *addr, size_t len)
686{
687
688	if (ofw_def_impl == NULL)
689		return (-1);
690
691	return (OFW_READ(ofw_obj, instance, addr, len));
692}
693
694/* Write to an instance. */
695ssize_t
696OF_write(ihandle_t instance, const void *addr, size_t len)
697{
698
699	if (ofw_def_impl == NULL)
700		return (-1);
701
702	return (OFW_WRITE(ofw_obj, instance, addr, len));
703}
704
705/* Seek to a position. */
706int
707OF_seek(ihandle_t instance, uint64_t pos)
708{
709
710	if (ofw_def_impl == NULL)
711		return (-1);
712
713	return (OFW_SEEK(ofw_obj, instance, pos));
714}
715
716/*
717 * Memory functions
718 */
719
720/* Claim an area of memory. */
721void *
722OF_claim(void *virt, size_t size, u_int align)
723{
724
725	if (ofw_def_impl == NULL)
726		return ((void *)-1);
727
728	return (OFW_CLAIM(ofw_obj, virt, size, align));
729}
730
731/* Release an area of memory. */
732void
733OF_release(void *virt, size_t size)
734{
735
736	if (ofw_def_impl == NULL)
737		return;
738
739	OFW_RELEASE(ofw_obj, virt, size);
740}
741
742/*
743 * Control transfer functions
744 */
745
746/* Suspend and drop back to the Open Firmware interface. */
747void
748OF_enter()
749{
750
751	if (ofw_def_impl == NULL)
752		return;
753
754	OFW_ENTER(ofw_obj);
755}
756
757/* Shut down and drop back to the Open Firmware interface. */
758void
759OF_exit()
760{
761
762	if (ofw_def_impl == NULL)
763		panic("OF_exit: Open Firmware not available");
764
765	/* Should not return */
766	OFW_EXIT(ofw_obj);
767
768	for (;;)			/* just in case */
769		;
770}
771