openfirm.c revision 256938
1275072Semaste/*	$NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $	*/
2275072Semaste
3275072Semaste/*-
4275072Semaste * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5275072Semaste * Copyright (C) 1995, 1996 TooLs GmbH.
6275072Semaste * All rights reserved.
7275072Semaste *
8275072Semaste * Redistribution and use in source and binary forms, with or without
9275072Semaste * modification, are permitted provided that the following conditions
10275072Semaste * are met:
11275072Semaste * 1. Redistributions of source code must retain the above copyright
12275072Semaste *    notice, this list of conditions and the following disclaimer.
13344779Sdim * 2. Redistributions in binary form must reproduce the above copyright
14275072Semaste *    notice, this list of conditions and the following disclaimer in the
15341825Sdim *    documentation and/or other materials provided with the distribution.
16275072Semaste * 3. All advertising materials mentioning features or use of this software
17275072Semaste *    must display the following acknowledgement:
18275072Semaste *	This product includes software developed by TooLs GmbH.
19275072Semaste * 4. The name of TooLs GmbH may not be used to endorse or promote products
20275072Semaste *    derived from this software without specific prior written permission.
21314564Sdim *
22314564Sdim * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23275072Semaste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24275072Semaste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25275072Semaste * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26341825Sdim * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27275072Semaste * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28275072Semaste * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29341825Sdim * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30341825Sdim * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31341825Sdim * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32314564Sdim */
33275072Semaste/*-
34314564Sdim * Copyright (C) 2000 Benno Rice.
35314564Sdim * All rights reserved.
36314564Sdim *
37314564Sdim * Redistribution and use in source and binary forms, with or without
38314564Sdim * modification, are permitted provided that the following conditions
39314564Sdim * are met:
40314564Sdim * 1. Redistributions of source code must retain the above copyright
41314564Sdim *    notice, this list of conditions and the following disclaimer.
42314564Sdim * 2. Redistributions in binary form must reproduce the above copyright
43314564Sdim *    notice, this list of conditions and the following disclaimer in the
44275072Semaste *    documentation and/or other materials provided with the distribution.
45314564Sdim *
46314564Sdim * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
47314564Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
48314564Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
49275072Semaste * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50275072Semaste * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51314564Sdim * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
52314564Sdim * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53314564Sdim * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
54314564Sdim * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
55314564Sdim * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56314564Sdim */
57314564Sdim
58314564Sdim#include <sys/cdefs.h>
59314564Sdim__FBSDID("$FreeBSD: head/sys/dev/ofw/openfirm.c 256938 2013-10-22 21:20:05Z nwhitehorn $");
60275072Semaste
61314564Sdim#include "opt_platform.h"
62275072Semaste
63314564Sdim#include <sys/param.h>
64314564Sdim#include <sys/kernel.h>
65314564Sdim#include <sys/malloc.h>
66314564Sdim#include <sys/systm.h>
67314564Sdim#include <sys/endian.h>
68314564Sdim
69314564Sdim#include <machine/stdarg.h>
70314564Sdim
71314564Sdim#include <dev/ofw/ofwvar.h>
72314564Sdim#include <dev/ofw/openfirm.h>
73275072Semaste
74314564Sdim#include "ofw_if.h"
75314564Sdim
76314564Sdimstatic void OF_putchar(int c, void *arg);
77275072Semaste
78314564SdimMALLOC_DEFINE(M_OFWPROP, "openfirm", "Open Firmware properties");
79314564Sdim
80314564Sdimstatic ihandle_t stdout;
81275072Semaste
82314564Sdimstatic ofw_def_t	*ofw_def_impl = NULL;
83314564Sdimstatic ofw_t		ofw_obj;
84275072Semastestatic struct ofw_kobj	ofw_kernel_obj;
85314564Sdimstatic struct kobj_ops	ofw_kernel_kops;
86314564Sdim
87275072Semaste/*
88314564Sdim * OFW install routines.  Highest priority wins, equal priority also
89314564Sdim * overrides allowing last-set to win.
90314564Sdim */
91275072SemasteSET_DECLARE(ofw_set, ofw_def_t);
92314564Sdim
93314564Sdimboolean_t
94314564SdimOF_install(char *name, int prio)
95314564Sdim{
96314564Sdim	ofw_def_t *ofwp, **ofwpp;
97314564Sdim	static int curr_prio = 0;
98314564Sdim
99314564Sdim	/*
100275072Semaste	 * Try and locate the OFW kobj corresponding to the name.
101314564Sdim	 */
102314564Sdim	SET_FOREACH(ofwpp, ofw_set) {
103314564Sdim		ofwp = *ofwpp;
104314564Sdim
105275072Semaste		if (ofwp->name &&
106314564Sdim		    !strcmp(ofwp->name, name) &&
107314564Sdim		    prio >= curr_prio) {
108275072Semaste			curr_prio = prio;
109314564Sdim			ofw_def_impl = ofwp;
110314564Sdim			return (TRUE);
111314564Sdim		}
112275072Semaste	}
113314564Sdim
114314564Sdim	return (FALSE);
115275072Semaste}
116314564Sdim
117314564Sdim/* Initializer */
118314564Sdimint
119275072SemasteOF_init(void *cookie)
120314564Sdim{
121314564Sdim	phandle_t chosen;
122314564Sdim	int rv;
123275072Semaste
124314564Sdim	if (ofw_def_impl == NULL)
125275072Semaste		return (-1);
126341825Sdim
127314564Sdim	ofw_obj = &ofw_kernel_obj;
128314564Sdim	/*
129314564Sdim	 * Take care of compiling the selected class, and
130314564Sdim	 * then statically initialize the OFW object.
131314564Sdim	 */
132314564Sdim	kobj_class_compile_static(ofw_def_impl, &ofw_kernel_kops);
133314564Sdim	kobj_init_static((kobj_t)ofw_obj, ofw_def_impl);
134275072Semaste
135341825Sdim	rv = OFW_INIT(ofw_obj, cookie);
136341825Sdim
137314564Sdim	if ((chosen = OF_finddevice("/chosen")) != -1)
138314564Sdim		if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1)
139314564Sdim			stdout = -1;
140314564Sdim
141314564Sdim	return (rv);
142314564Sdim}
143314564Sdim
144314564Sdimstatic void
145314564SdimOF_putchar(int c, void *arg __unused)
146314564Sdim{
147314564Sdim	char cbuf;
148275072Semaste
149314564Sdim	if (c == '\n') {
150275072Semaste		cbuf = '\r';
151314564Sdim		OF_write(stdout, &cbuf, 1);
152275072Semaste	}
153314564Sdim
154314564Sdim	cbuf = c;
155314564Sdim	OF_write(stdout, &cbuf, 1);
156314564Sdim}
157314564Sdim
158314564Sdimvoid
159314564SdimOF_printf(const char *fmt, ...)
160314564Sdim{
161314564Sdim	va_list	va;
162314564Sdim
163314564Sdim	va_start(va, fmt);
164275072Semaste	(void)kvprintf(fmt, OF_putchar, NULL, 10, va);
165314564Sdim	va_end(va);
166275072Semaste}
167275072Semaste
168314564Sdim/*
169275072Semaste * Generic functions
170314564Sdim */
171314564Sdim
172314564Sdim/* Test to see if a service exists. */
173275072Semasteint
174314564SdimOF_test(const char *name)
175314564Sdim{
176314564Sdim
177314564Sdim	if (ofw_def_impl == NULL)
178275072Semaste		return (-1);
179314564Sdim
180314564Sdim	return (OFW_TEST(ofw_obj, name));
181314564Sdim}
182314564Sdim
183314564Sdimint
184275072SemasteOF_interpret(const char *cmd, int nreturns, ...)
185314564Sdim{
186275072Semaste	va_list ap;
187314564Sdim	cell_t slots[16];
188314564Sdim	int i = 0;
189275072Semaste	int status;
190314564Sdim
191314564Sdim	if (ofw_def_impl == NULL)
192275072Semaste		return (-1);
193314564Sdim
194314564Sdim	status = OFW_INTERPRET(ofw_obj, cmd, nreturns, slots);
195314564Sdim	if (status == -1)
196275072Semaste		return (status);
197314564Sdim
198314564Sdim	va_start(ap, nreturns);
199275072Semaste	while (i < nreturns)
200341825Sdim		*va_arg(ap, cell_t *) = slots[i++];
201341825Sdim	va_end(ap);
202314564Sdim
203275072Semaste	return (status);
204314564Sdim}
205314564Sdim
206314564Sdim/*
207275072Semaste * Device tree functions
208314564Sdim */
209314564Sdim
210275072Semaste/* Return the next sibling of this node or 0. */
211314564Sdimphandle_t
212314564SdimOF_peer(phandle_t node)
213314564Sdim{
214314564Sdim
215314564Sdim	if (ofw_def_impl == NULL)
216275072Semaste		return (0);
217314564Sdim
218314564Sdim	return (OFW_PEER(ofw_obj, node));
219314564Sdim}
220314564Sdim
221314564Sdim/* Return the first child of this node or 0. */
222275072Semastephandle_t
223314564SdimOF_child(phandle_t node)
224314564Sdim{
225314564Sdim
226275072Semaste	if (ofw_def_impl == NULL)
227314564Sdim		return (0);
228314564Sdim
229275072Semaste	return (OFW_CHILD(ofw_obj, node));
230314564Sdim}
231314564Sdim
232314564Sdim/* Return the parent of this node or 0. */
233275072Semastephandle_t
234314564SdimOF_parent(phandle_t node)
235275072Semaste{
236314564Sdim
237275072Semaste	if (ofw_def_impl == NULL)
238314564Sdim		return (0);
239314564Sdim
240314564Sdim	return (OFW_PARENT(ofw_obj, node));
241275072Semaste}
242314564Sdim
243275072Semaste/* Return the package handle that corresponds to an instance handle. */
244314564Sdimphandle_t
245275072SemasteOF_instance_to_package(ihandle_t instance)
246275072Semaste{
247275072Semaste
248	if (ofw_def_impl == NULL)
249		return (-1);
250
251	return (OFW_INSTANCE_TO_PACKAGE(ofw_obj, instance));
252}
253
254/* Get the length of a property of a package. */
255ssize_t
256OF_getproplen(phandle_t package, const char *propname)
257{
258
259	if (ofw_def_impl == NULL)
260		return (-1);
261
262	return (OFW_GETPROPLEN(ofw_obj, package, propname));
263}
264
265/* Check existence of a property of a package. */
266int
267OF_hasprop(phandle_t package, const char *propname)
268{
269
270	return (OF_getproplen(package, propname) >= 0 ? 1 : 0);
271}
272
273/* Get the value of a property of a package. */
274ssize_t
275OF_getprop(phandle_t package, const char *propname, void *buf, size_t buflen)
276{
277
278	if (ofw_def_impl == NULL)
279		return (-1);
280
281	return (OFW_GETPROP(ofw_obj, package, propname, buf, buflen));
282}
283
284ssize_t
285OF_getencprop(phandle_t node, const char *propname, pcell_t *buf, size_t len)
286{
287	ssize_t retval;
288	int i;
289
290	KASSERT(len % 4 == 0, "Need a multiple of 4 bytes");
291
292	retval = OF_getprop(node, propname, buf, len);
293	for (i = 0; i < len/4; i++)
294		buf[i] = be32toh(buf[i]);
295
296	return (retval);
297}
298
299/*
300 * Recursively search the node and its parent for the given property, working
301 * downward from the node to the device tree root.  Returns the value of the
302 * first match.
303 */
304ssize_t
305OF_searchprop(phandle_t node, const char *propname, void *buf, size_t len)
306{
307	ssize_t rv;
308
309	for (; node != 0; node = OF_parent(node))
310		if ((rv = OF_getprop(node, propname, buf, len)) != -1)
311			return (rv);
312	return (-1);
313}
314
315ssize_t
316OF_searchencprop(phandle_t node, const char *propname, void *buf, size_t len)
317{
318	ssize_t rv;
319
320	for (; node != 0; node = OF_parent(node))
321		if ((rv = OF_getencprop(node, propname, buf, len)) != -1)
322			return (rv);
323	return (-1);
324}
325
326/*
327 * Store the value of a property of a package into newly allocated memory
328 * (using the M_OFWPROP malloc pool and M_WAITOK).  elsz is the size of a
329 * single element, the number of elements is return in number.
330 */
331ssize_t
332OF_getprop_alloc(phandle_t package, const char *propname, int elsz, void **buf)
333{
334	int len;
335
336	*buf = NULL;
337	if ((len = OF_getproplen(package, propname)) == -1 ||
338	    len % elsz != 0)
339		return (-1);
340
341	*buf = malloc(len, M_OFWPROP, M_WAITOK);
342	if (OF_getprop(package, propname, *buf, len) == -1) {
343		free(*buf, M_OFWPROP);
344		*buf = NULL;
345		return (-1);
346	}
347	return (len / elsz);
348}
349
350ssize_t
351OF_getencprop_alloc(phandle_t package, const char *name, int elsz, void **buf)
352{
353	ssize_t retval;
354	pcell_t *cell;
355	int i;
356
357	KASSERT(elsz % 4 == 0, "Need a multiple of 4 bytes");
358
359	retval = OF_getprop_alloc(package, name, elsz, buf);
360	if (retval == -1)
361		return (retval);
362
363	cell = *buf;
364	for (i = 0; i < retval*elsz/4; i++)
365		cell[i] = be32toh(cell[i]);
366
367	return (retval);
368}
369
370/* Get the next property of a package. */
371int
372OF_nextprop(phandle_t package, const char *previous, char *buf, size_t size)
373{
374
375	if (ofw_def_impl == NULL)
376		return (-1);
377
378	return (OFW_NEXTPROP(ofw_obj, package, previous, buf, size));
379}
380
381/* Set the value of a property of a package. */
382int
383OF_setprop(phandle_t package, const char *propname, const void *buf, size_t len)
384{
385
386	if (ofw_def_impl == NULL)
387		return (-1);
388
389	return (OFW_SETPROP(ofw_obj, package, propname, buf,len));
390}
391
392/* Convert a device specifier to a fully qualified pathname. */
393ssize_t
394OF_canon(const char *device, char *buf, size_t len)
395{
396
397	if (ofw_def_impl == NULL)
398		return (-1);
399
400	return (OFW_CANON(ofw_obj, device, buf, len));
401}
402
403/* Return a package handle for the specified device. */
404phandle_t
405OF_finddevice(const char *device)
406{
407
408	if (ofw_def_impl == NULL)
409		return (-1);
410
411	return (OFW_FINDDEVICE(ofw_obj, device));
412}
413
414/* Return the fully qualified pathname corresponding to an instance. */
415ssize_t
416OF_instance_to_path(ihandle_t instance, char *buf, size_t len)
417{
418
419	if (ofw_def_impl == NULL)
420		return (-1);
421
422	return (OFW_INSTANCE_TO_PATH(ofw_obj, instance, buf, len));
423}
424
425/* Return the fully qualified pathname corresponding to a package. */
426ssize_t
427OF_package_to_path(phandle_t package, char *buf, size_t len)
428{
429
430	if (ofw_def_impl == NULL)
431		return (-1);
432
433	return (OFW_PACKAGE_TO_PATH(ofw_obj, package, buf, len));
434}
435
436/* Look up effective phandle (see FDT/PAPR spec) */
437static phandle_t
438OF_child_xref_phandle(phandle_t parent, phandle_t xref)
439{
440	phandle_t child, rxref;
441
442	/*
443	 * Recursively descend from parent, looking for a node with a property
444	 * named either "phandle", "ibm,phandle", or "linux,phandle" that
445	 * matches the xref we are looking for.
446	 */
447
448	for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
449		rxref = OF_child_xref_phandle(child, xref);
450		if (rxref != -1)
451			return (rxref);
452
453		if (OF_getprop(child, "phandle", &rxref, sizeof(rxref)) == -1 &&
454		    OF_getprop(child, "ibm,phandle", &rxref,
455		    sizeof(rxref)) == -1 && OF_getprop(child,
456		    "linux,phandle", &rxref, sizeof(rxref)) == -1)
457			continue;
458
459		if (rxref == xref)
460			return (child);
461	}
462
463	return (-1);
464}
465
466phandle_t
467OF_xref_phandle(phandle_t xref)
468{
469	phandle_t node;
470
471	node = OF_child_xref_phandle(OF_peer(0), xref);
472	if (node == -1)
473		return (xref);
474
475	return (node);
476}
477
478/*  Call the method in the scope of a given instance. */
479int
480OF_call_method(const char *method, ihandle_t instance, int nargs, int nreturns,
481    ...)
482{
483	va_list ap;
484	cell_t args_n_results[12];
485	int n, status;
486
487	if (nargs > 6 || ofw_def_impl == NULL)
488		return (-1);
489	va_start(ap, nreturns);
490	for (n = 0; n < nargs; n++)
491		args_n_results[n] = va_arg(ap, cell_t);
492
493	status = OFW_CALL_METHOD(ofw_obj, instance, method, nargs, nreturns,
494	    args_n_results);
495	if (status != 0)
496		return (status);
497
498	for (; n < nargs + nreturns; n++)
499		*va_arg(ap, cell_t *) = args_n_results[n];
500	va_end(ap);
501	return (0);
502}
503
504/*
505 * Device I/O functions
506 */
507
508/* Open an instance for a device. */
509ihandle_t
510OF_open(const char *device)
511{
512
513	if (ofw_def_impl == NULL)
514		return (0);
515
516	return (OFW_OPEN(ofw_obj, device));
517}
518
519/* Close an instance. */
520void
521OF_close(ihandle_t instance)
522{
523
524	if (ofw_def_impl == NULL)
525		return;
526
527	OFW_CLOSE(ofw_obj, instance);
528}
529
530/* Read from an instance. */
531ssize_t
532OF_read(ihandle_t instance, void *addr, size_t len)
533{
534
535	if (ofw_def_impl == NULL)
536		return (-1);
537
538	return (OFW_READ(ofw_obj, instance, addr, len));
539}
540
541/* Write to an instance. */
542ssize_t
543OF_write(ihandle_t instance, const void *addr, size_t len)
544{
545
546	if (ofw_def_impl == NULL)
547		return (-1);
548
549	return (OFW_WRITE(ofw_obj, instance, addr, len));
550}
551
552/* Seek to a position. */
553int
554OF_seek(ihandle_t instance, uint64_t pos)
555{
556
557	if (ofw_def_impl == NULL)
558		return (-1);
559
560	return (OFW_SEEK(ofw_obj, instance, pos));
561}
562
563/*
564 * Memory functions
565 */
566
567/* Claim an area of memory. */
568void *
569OF_claim(void *virt, size_t size, u_int align)
570{
571
572	if (ofw_def_impl == NULL)
573		return ((void *)-1);
574
575	return (OFW_CLAIM(ofw_obj, virt, size, align));
576}
577
578/* Release an area of memory. */
579void
580OF_release(void *virt, size_t size)
581{
582
583	if (ofw_def_impl == NULL)
584		return;
585
586	OFW_RELEASE(ofw_obj, virt, size);
587}
588
589/*
590 * Control transfer functions
591 */
592
593/* Suspend and drop back to the Open Firmware interface. */
594void
595OF_enter()
596{
597
598	if (ofw_def_impl == NULL)
599		return;
600
601	OFW_ENTER(ofw_obj);
602}
603
604/* Shut down and drop back to the Open Firmware interface. */
605void
606OF_exit()
607{
608
609	if (ofw_def_impl == NULL)
610		panic("OF_exit: Open Firmware not available");
611
612	/* Should not return */
613	OFW_EXIT(ofw_obj);
614
615	for (;;)			/* just in case */
616		;
617}
618