]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ofw/openfirm.c
Add OF_xref_from_node_strict() which returns -1 if there is no xref handle
[FreeBSD/FreeBSD.git] / sys / dev / ofw / openfirm.c
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$");
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
76 static void OF_putchar(int c, void *arg);
77
78 MALLOC_DEFINE(M_OFWPROP, "openfirm", "Open Firmware properties");
79
80 static ihandle_t stdout;
81
82 static ofw_def_t        *ofw_def_impl = NULL;
83 static ofw_t            ofw_obj;
84 static struct ofw_kobj  ofw_kernel_obj;
85 static struct kobj_ops  ofw_kernel_kops;
86
87 struct xrefinfo {
88         phandle_t       xref;
89         phandle_t       node;
90         device_t        dev;
91         SLIST_ENTRY(xrefinfo) next_entry;
92 };
93
94 static SLIST_HEAD(, xrefinfo) xreflist = SLIST_HEAD_INITIALIZER(xreflist);
95 static 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  */
112 static void
113 xrefinfo_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
137 static void
138 xrefinfo_init(void *unsed)
139 {
140
141         xrefinfo_create(OF_peer(0));
142         xref_init_done = true;
143 }
144 SYSINIT(xrefinfo, SI_SUB_KMEM, SI_ORDER_ANY, xrefinfo_init, NULL);
145
146 static struct xrefinfo *
147 xrefinfo_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  */
166 SET_DECLARE(ofw_set, ofw_def_t);
167
168 boolean_t
169 OF_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 */
193 int
194 OF_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
220 static void
221 OF_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
234 void
235 OF_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. */
249 int
250 OF_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
259 int
260 OF_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. */
287 phandle_t
288 OF_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. */
298 phandle_t
299 OF_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. */
309 phandle_t
310 OF_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. */
320 phandle_t
321 OF_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. */
331 ssize_t
332 OF_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. */
342 int
343 OF_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. */
350 ssize_t
351 OF_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
360 ssize_t
361 OF_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  */
380 ssize_t
381 OF_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
391 ssize_t
392 OF_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  */
407 ssize_t
408 OF_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
426 ssize_t
427 OF_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. */
445 int
446 OF_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. */
456 int
457 OF_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. */
467 ssize_t
468 OF_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. */
478 phandle_t
479 OF_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. */
489 ssize_t
490 OF_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. */
500 ssize_t
501 OF_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) */
511 static phandle_t
512 OF_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
540 phandle_t
541 OF_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
557 static phandle_t
558 xref_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
577 phandle_t
578 OF_xref_from_node(phandle_t node)
579 {
580
581         return (xref_from_node(node, node));
582 }
583
584 phandle_t
585 OF_xref_from_node_strict(phandle_t node)
586 {
587
588         return (xref_from_node(node, -1));
589 }
590
591 device_t
592 OF_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
604 phandle_t
605 OF_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
617 int
618 OF_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. */
632 int
633 OF_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. */
662 ihandle_t
663 OF_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. */
673 void
674 OF_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. */
684 ssize_t
685 OF_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. */
695 ssize_t
696 OF_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. */
706 int
707 OF_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. */
721 void *
722 OF_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. */
732 void
733 OF_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. */
747 void
748 OF_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. */
758 void
759 OF_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 }