~vojtech-horky/helenos/numa

« back to all changes in this revision

Viewing changes to boot/genarch/ofw_tree.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2006 Jakub Jermar
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * - Redistributions of source code must retain the above copyright
 
10
 *   notice, this list of conditions and the following disclaimer.
 
11
 * - Redistributions in binary form must reproduce the above copyright
 
12
 *   notice, this list of conditions and the following disclaimer in the
 
13
 *   documentation and/or other materials provided with the distribution.
 
14
 * - The name of the author may not be used to endorse or promote products
 
15
 *   derived from this software without specific prior written permission.
 
16
 *
 
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
27
 */
 
28
 
 
29
#include <ofw_tree.h>
 
30
#include <ofw.h>
 
31
#include <types.h>
 
32
#include <string.h>
 
33
#include <balloc.h>
 
34
#include <asm.h>
 
35
 
 
36
#define MAX_PATH_LEN    256
 
37
 
 
38
static ofw_tree_node_t *ofw_tree_node_alloc(void)
 
39
{
 
40
        return balloc(sizeof(ofw_tree_node_t), sizeof(ofw_tree_node_t));
 
41
}
 
42
 
 
43
static ofw_tree_property_t *ofw_tree_properties_alloc(unsigned count)
 
44
{
 
45
        return balloc(count * sizeof(ofw_tree_property_t),
 
46
            sizeof(ofw_tree_property_t));
 
47
}
 
48
 
 
49
static void *ofw_tree_space_alloc(size_t size)
 
50
{
 
51
        char *addr;
 
52
 
 
53
        /*
 
54
         * What we do here is a nasty hack :-)
 
55
         * Problem: string property values that are allocated via this
 
56
         * function typically do not contain the trailing '\0'. This
 
57
         * is very uncomfortable for kernel, which is supposed to deal
 
58
         * with the properties.
 
59
         * Solution: when allocating space via this function, we always
 
60
         * allocate space for the extra '\0' character that we store
 
61
         * behind the requested memory.
 
62
         */
 
63
        addr = balloc(size + 1, size);
 
64
        if (addr)
 
65
                addr[size] = '\0';
 
66
        return addr;
 
67
}
 
68
 
 
69
/** Transfer information from one OpenFirmware node into its memory
 
70
 * representation.
 
71
 *
 
72
 * Transfer entire information from the OpenFirmware device tree 'current' node
 
73
 * to its memory representation in 'current_node'. This function recursively
 
74
 * processes all node's children. Node's peers are processed iteratively in
 
75
 * order to prevent stack from overflowing.
 
76
 *
 
77
 * @param current_node  Pointer to uninitialized ofw_tree_node structure that
 
78
 *                      will become the memory represenation of 'current'.
 
79
 * @param parent_node   Parent ofw_tree_node structure or NULL in case of root
 
80
 *                      node.
 
81
 * @param current       OpenFirmware phandle to the current device tree node.
 
82
 */
 
83
static void ofw_tree_node_process(ofw_tree_node_t *current_node,
 
84
    ofw_tree_node_t *parent_node, phandle current)
 
85
{
 
86
        static char path[MAX_PATH_LEN + 1];
 
87
        static char name[OFW_TREE_PROPERTY_MAX_NAMELEN];
 
88
        static char name2[OFW_TREE_PROPERTY_MAX_NAMELEN];
 
89
        phandle peer;
 
90
        phandle child;
 
91
        size_t len;
 
92
        int i;
 
93
 
 
94
        while (current_node) {
 
95
                /*
 
96
                 * Initialize node.
 
97
                 */
 
98
                current_node->parent = parent_node;
 
99
                current_node->peer = NULL;
 
100
                current_node->child = NULL;
 
101
                current_node->node_handle = current;
 
102
                current_node->properties = 0;
 
103
                current_node->property = NULL;
 
104
                current_node->device = NULL;
 
105
        
 
106
                /*
 
107
                 * Get the disambigued name.
 
108
                 */
 
109
                len = ofw_package_to_path(current, path, MAX_PATH_LEN);
 
110
                if (len == -1)
 
111
                        return;
 
112
        
 
113
                path[len] = '\0';
 
114
                for (i = len - 1; i >= 0 && path[i] != '/'; i--)
 
115
                        ;
 
116
                i++;    /* do not include '/' */
 
117
        
 
118
                len -= i;
 
119
 
 
120
                /* add space for trailing '\0' */
 
121
                current_node->da_name = ofw_tree_space_alloc(len + 1);
 
122
                if (!current_node->da_name)
 
123
                        return;
 
124
        
 
125
                memcpy(current_node->da_name, &path[i], len);
 
126
                current_node->da_name[len] = '\0';
 
127
        
 
128
                /*
 
129
                 * Recursively process the potential child node.
 
130
                 */
 
131
                child = ofw_get_child_node(current);
 
132
                if (child != 0 && child != -1) {
 
133
                        ofw_tree_node_t *child_node;
 
134
                
 
135
                        child_node = ofw_tree_node_alloc();
 
136
                        if (child_node) {
 
137
                                ofw_tree_node_process(child_node, current_node,
 
138
                                    child);
 
139
                                current_node->child = child_node;
 
140
                        }
 
141
                }
 
142
        
 
143
                /*
 
144
                 * Count properties.
 
145
                 */
 
146
                name[0] = '\0';
 
147
                while (ofw_next_property(current, name, name2) == 1) {
 
148
                        current_node->properties++;
 
149
                        memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN);
 
150
                }
 
151
 
 
152
                if (!current_node->properties)
 
153
                        return;
 
154
        
 
155
                /*
 
156
                 * Copy properties.
 
157
                 */
 
158
                current_node->property =
 
159
                    ofw_tree_properties_alloc(current_node->properties);
 
160
                if (!current_node->property)
 
161
                        return;
 
162
                
 
163
                name[0] = '\0';
 
164
                for (i = 0; ofw_next_property(current, name, name2) == 1; i++) {
 
165
                        size_t size;
 
166
                
 
167
                        if (i == current_node->properties)
 
168
                                break;
 
169
                
 
170
                        memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN);
 
171
                        memcpy(current_node->property[i].name, name,
 
172
                            OFW_TREE_PROPERTY_MAX_NAMELEN);
 
173
                        current_node->property[i].name[
 
174
                            OFW_TREE_PROPERTY_MAX_NAMELEN] = '\0';
 
175
 
 
176
                        size = ofw_get_proplen(current, name);
 
177
                        current_node->property[i].size = size;
 
178
                        if (size) {
 
179
                                void *buf;
 
180
                        
 
181
                                buf = ofw_tree_space_alloc(size);
 
182
                                if (current_node->property[i].value = buf) {
 
183
                                        /*
 
184
                                         * Copy property value to memory node.
 
185
                                         */
 
186
                                        (void) ofw_get_property(current, name,
 
187
                                            buf, size);
 
188
                                }
 
189
                        } else {
 
190
                                current_node->property[i].value = NULL;
 
191
                        }
 
192
                }
 
193
 
 
194
                /* Just in case we ran out of memory. */
 
195
                current_node->properties = i;
 
196
 
 
197
                /*
 
198
                 * Iteratively process the next peer node.
 
199
                 * Note that recursion is a bad idea here.
 
200
                 * Due to the topology of the OpenFirmware device tree,
 
201
                 * the nesting of peer nodes could be to wide and the
 
202
                 * risk of overflowing the stack is too real.
 
203
                 */
 
204
                peer = ofw_get_peer_node(current);
 
205
                if (peer != 0 && peer != -1) {
 
206
                        ofw_tree_node_t *peer_node;
 
207
                
 
208
                        peer_node = ofw_tree_node_alloc();
 
209
                        if (peer_node) { 
 
210
                                current_node->peer = peer_node;
 
211
                                current_node = peer_node;
 
212
                                current = peer;
 
213
                                /*
 
214
                                 * Process the peer in next iteration.
 
215
                                 */
 
216
                                continue;
 
217
                        }
 
218
                }
 
219
                /*
 
220
                 * No more peers on this level.
 
221
                 */
 
222
                break;
 
223
        }
 
224
}
 
225
 
 
226
/** Construct memory representation of OpenFirmware device tree.
 
227
 *
 
228
 * @return              NULL on failure or pointer to the root node.
 
229
 */
 
230
ofw_tree_node_t *ofw_tree_build(void)
 
231
{
 
232
        ofw_tree_node_t *root;
 
233
        phandle ssm_node;
 
234
        ofw_tree_node_t *ssm;
 
235
        
 
236
        root = ofw_tree_node_alloc();
 
237
        if (root)
 
238
                ofw_tree_node_process(root, NULL, ofw_root);
 
239
 
 
240
        /*
 
241
         * The firmware client interface does not automatically include the
 
242
         * "ssm" node in the list of children of "/". A nasty yet working
 
243
         * solution is to explicitly stick "ssm" to the OFW tree.
 
244
         */
 
245
        ssm_node = ofw_find_device("/ssm@0,0");
 
246
        if (ssm_node != -1) {
 
247
                ssm = ofw_tree_node_alloc();
 
248
                if (ssm) {
 
249
                        ofw_tree_node_process(ssm, root,
 
250
                            ofw_find_device("/ssm@0,0"));
 
251
                        ssm->peer = root->child;
 
252
                        root->child = ssm;
 
253
                }
 
254
        }
 
255
        
 
256
        return root;
 
257
}