Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:09

0001 /*
0002  * Copyright (c) 2006 Jakub Jermar
0003  * All rights reserved.
0004  *
0005  * Redistribution and use in source and binary forms, with or without
0006  * modification, are permitted provided that the following conditions
0007  * are met:
0008  *
0009  * - Redistributions of source code must retain the above copyright
0010  *   notice, this list of conditions and the following disclaimer.
0011  * - Redistributions in binary form must reproduce the above copyright
0012  *   notice, this list of conditions and the following disclaimer in the
0013  *   documentation and/or other materials provided with the distribution.
0014  * - The name of the author may not be used to endorse or promote products
0015  *   derived from this software without specific prior written permission.
0016  *
0017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027  */
0028 
0029 /*
0030  * Modifications are made to compile for RTEMS. Removes asm.h and memstr.h.
0031  *
0032  */
0033 
0034 
0035 #include <boot/ofw_tree.h>
0036 #include <boot/ofw.h>
0037 #include <boot/ofwarch.h>
0038 #include <boot/types.h>
0039 #include <string.h>
0040 #include <boot/balloc.h>
0041 #if 0
0042 #include <asm.h>
0043 #include <memstr.h>
0044 #endif
0045 
0046 static ofw_tree_node_t *ofw_tree_node_alloc(void)
0047 {
0048     return balloc(sizeof(ofw_tree_node_t), sizeof(ofw_tree_node_t));
0049 }
0050 
0051 static ofw_tree_property_t *ofw_tree_properties_alloc(unsigned count)
0052 {
0053     return balloc(count * sizeof(ofw_tree_property_t),
0054         sizeof(ofw_tree_property_t));
0055 }
0056 
0057 static void *ofw_tree_space_alloc(size_t size)
0058 {
0059     /*
0060      * What we do here is a nasty hack :-)
0061      * Problem: string property values that are allocated via this
0062      * function typically do not contain the trailing '\0'. This
0063      * is very uncomfortable for kernel, which is supposed to deal
0064      * with the properties.
0065      * Solution: when allocating space via this function, we always
0066      * allocate space for the extra '\0' character that we store
0067      * behind the requested memory.
0068      */
0069     char *addr = balloc(size + 1, size);
0070     if (addr)
0071         addr[size] = '\0';
0072     
0073     return addr;
0074 }
0075 
0076 /** Transfer information from one OpenFirmware node into its memory
0077  * representation.
0078  *
0079  * Transfer entire information from the OpenFirmware device tree 'current' node
0080  * to its memory representation in 'current_node'. This function recursively
0081  * processes all node's children. Node's peers are processed iteratively in
0082  * order to prevent stack from overflowing.
0083  *
0084  * @param current_node Pointer to uninitialized ofw_tree_node structure that
0085  *                     will become the memory represenation of 'current'.
0086  * @param parent_node  Parent ofw_tree_node structure or NULL in case of root
0087  *                     node.
0088  * @param current      OpenFirmware phandle to the current device tree node.
0089  *
0090  */
0091 static void ofw_tree_node_process(ofw_tree_node_t *current_node,
0092     ofw_tree_node_t *parent_node, phandle current)
0093 {
0094     while (current_node) {
0095         /*
0096          * Initialize node.
0097          */
0098         current_node->parent = (ofw_tree_node_t *) balloc_rebase(parent_node);
0099         current_node->peer = NULL;
0100         current_node->child = NULL;
0101         current_node->node_handle = current;
0102         current_node->properties = 0;
0103         current_node->property = NULL;
0104         current_node->device = NULL;
0105         
0106         /*
0107          * Get the disambigued name.
0108          */
0109         static char path[OFW_TREE_PATH_MAX_LEN + 1];
0110         size_t len = ofw_package_to_path(current, path, OFW_TREE_PATH_MAX_LEN);
0111         if (len == -1)
0112             return;
0113         
0114         path[len] = '\0';
0115         
0116         /* Find last slash */
0117         int i;
0118         for (i = len - 1; (i >= 0) && (path[i] != '/'); i--);
0119         
0120         /* Do not include the slash */
0121         i++;
0122         len -= i;
0123         
0124         /* Add space for trailing '\0' */
0125         char *da_name = ofw_tree_space_alloc(len + 1);
0126         if (!da_name)
0127             return;
0128         
0129         memcpy(da_name, &path[i], len);
0130         da_name[len] = '\0';
0131         current_node->da_name = (char *) balloc_rebase(da_name);
0132         
0133         /*
0134          * Recursively process the potential child node.
0135          */
0136         phandle child = ofw_get_child_node(current);
0137         if ((child != 0) && (child != -1)) {
0138             ofw_tree_node_t *child_node = ofw_tree_node_alloc();
0139             if (child_node) {
0140                 ofw_tree_node_process(child_node, current_node,
0141                     child);
0142                 current_node->child =
0143                     (ofw_tree_node_t *) balloc_rebase(child_node);
0144             }
0145         }
0146         
0147         /*
0148          * Count properties.
0149          */
0150         static char name[OFW_TREE_PROPERTY_MAX_NAMELEN];
0151         static char name2[OFW_TREE_PROPERTY_MAX_NAMELEN];
0152         name[0] = '\0';
0153         while (ofw_next_property(current, name, name2) == 1) {
0154             current_node->properties++;
0155             memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN);
0156         }
0157         
0158         if (!current_node->properties)
0159             return;
0160         
0161         /*
0162          * Copy properties.
0163          */
0164         ofw_tree_property_t *property =
0165             ofw_tree_properties_alloc(current_node->properties);
0166         if (!property)
0167             return;
0168         
0169         name[0] = '\0';
0170         for (i = 0; ofw_next_property(current, name, name2) == 1; i++) {
0171             if (i == current_node->properties)
0172                 break;
0173             
0174             memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN);
0175             memcpy(property[i].name, name, OFW_TREE_PROPERTY_MAX_NAMELEN);
0176             property[i].name[OFW_TREE_PROPERTY_MAX_NAMELEN - 1] = '\0';
0177             
0178             size_t size = ofw_get_proplen(current, name);
0179             property[i].size = size;
0180             
0181             if (size) {
0182                 void *buf = ofw_tree_space_alloc(size);
0183                 if (buf) {
0184                     /*
0185                      * Copy property value to memory node.
0186                      */
0187                     (void) ofw_get_property(current, name, buf, size);
0188                     property[i].value = balloc_rebase(buf);
0189                 }
0190             } else
0191                 property[i].value = NULL;
0192         }
0193         
0194         /* Just in case we ran out of memory. */
0195         current_node->properties = i;
0196         current_node->property = (ofw_tree_property_t *) balloc_rebase(property);
0197         
0198         
0199         /*
0200          * Iteratively process the next peer node.
0201          * Note that recursion is a bad idea here.
0202          * Due to the topology of the OpenFirmware device tree,
0203          * the nesting of peer nodes could be to wide and the
0204          * risk of overflowing the stack is too real.
0205          */
0206         phandle peer = ofw_get_peer_node(current);
0207         if ((peer != 0) && (peer != -1)) {
0208             ofw_tree_node_t *peer_node = ofw_tree_node_alloc();
0209             if (peer_node) {
0210                 current_node->peer = (ofw_tree_node_t *) balloc_rebase(peer_node);
0211                 current_node = peer_node;
0212                 current = peer;
0213                 /*
0214                  * Process the peer in next iteration.
0215                  */
0216                 continue;
0217             }
0218         }
0219         
0220         /*
0221          * No more peers on this level.
0222          */
0223         break;
0224     }
0225 }
0226 
0227 /** Construct memory representation of OpenFirmware device tree.
0228  *
0229  * @return NULL on failure or kernel pointer to the root node.
0230  *
0231  */
0232 ofw_tree_node_t *ofw_tree_build(void)
0233 {
0234     ofw_tree_node_t *root = ofw_tree_node_alloc();
0235     if (root)
0236         ofw_tree_node_process(root, NULL, ofw_root);
0237     
0238     /*
0239      * The firmware client interface does not automatically include the
0240      * "ssm" node in the list of children of "/". A nasty yet working
0241      * solution is to explicitly stick "ssm" to the OFW tree.
0242      */
0243     phandle ssm_node = ofw_find_device("/ssm@0,0");
0244     if (ssm_node != -1) {
0245         ofw_tree_node_t *ssm = ofw_tree_node_alloc();
0246         if (ssm) {
0247             ofw_tree_node_process(ssm, root,
0248                 ofw_find_device("/ssm@0,0"));
0249             ssm->peer = root->child;
0250             root->child = (ofw_tree_node_t *) balloc_rebase(ssm);
0251         }
0252     }
0253     
0254     return (ofw_tree_node_t *) balloc_rebase(root);
0255 }