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 | /* |
---|
30 | * Modifications are made to compile for RTEMS. Removes asm.h and memstr.h. |
---|
31 | * |
---|
32 | */ |
---|
33 | |
---|
34 | |
---|
35 | #include <boot/ofw_tree.h> |
---|
36 | #include <boot/ofw.h> |
---|
37 | #include <boot/ofwarch.h> |
---|
38 | #include <boot/types.h> |
---|
39 | #include <string.h> |
---|
40 | #include <boot/balloc.h> |
---|
41 | #if 0 |
---|
42 | #include <asm.h> |
---|
43 | #include <memstr.h> |
---|
44 | #endif |
---|
45 | |
---|
46 | static ofw_tree_node_t *ofw_tree_node_alloc(void) |
---|
47 | { |
---|
48 | return balloc(sizeof(ofw_tree_node_t), sizeof(ofw_tree_node_t)); |
---|
49 | } |
---|
50 | |
---|
51 | static ofw_tree_property_t *ofw_tree_properties_alloc(unsigned count) |
---|
52 | { |
---|
53 | return balloc(count * sizeof(ofw_tree_property_t), |
---|
54 | sizeof(ofw_tree_property_t)); |
---|
55 | } |
---|
56 | |
---|
57 | static void *ofw_tree_space_alloc(size_t size) |
---|
58 | { |
---|
59 | /* |
---|
60 | * What we do here is a nasty hack :-) |
---|
61 | * Problem: string property values that are allocated via this |
---|
62 | * function typically do not contain the trailing '\0'. This |
---|
63 | * is very uncomfortable for kernel, which is supposed to deal |
---|
64 | * with the properties. |
---|
65 | * Solution: when allocating space via this function, we always |
---|
66 | * allocate space for the extra '\0' character that we store |
---|
67 | * behind the requested memory. |
---|
68 | */ |
---|
69 | char *addr = balloc(size + 1, size); |
---|
70 | if (addr) |
---|
71 | addr[size] = '\0'; |
---|
72 | |
---|
73 | return addr; |
---|
74 | } |
---|
75 | |
---|
76 | /** Transfer information from one OpenFirmware node into its memory |
---|
77 | * representation. |
---|
78 | * |
---|
79 | * Transfer entire information from the OpenFirmware device tree 'current' node |
---|
80 | * to its memory representation in 'current_node'. This function recursively |
---|
81 | * processes all node's children. Node's peers are processed iteratively in |
---|
82 | * order to prevent stack from overflowing. |
---|
83 | * |
---|
84 | * @param current_node Pointer to uninitialized ofw_tree_node structure that |
---|
85 | * will become the memory represenation of 'current'. |
---|
86 | * @param parent_node Parent ofw_tree_node structure or NULL in case of root |
---|
87 | * node. |
---|
88 | * @param current OpenFirmware phandle to the current device tree node. |
---|
89 | * |
---|
90 | */ |
---|
91 | static void ofw_tree_node_process(ofw_tree_node_t *current_node, |
---|
92 | ofw_tree_node_t *parent_node, phandle current) |
---|
93 | { |
---|
94 | while (current_node) { |
---|
95 | /* |
---|
96 | * Initialize node. |
---|
97 | */ |
---|
98 | current_node->parent = (ofw_tree_node_t *) balloc_rebase(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 | static char path[OFW_TREE_PATH_MAX_LEN + 1]; |
---|
110 | size_t len = ofw_package_to_path(current, path, OFW_TREE_PATH_MAX_LEN); |
---|
111 | if (len == -1) |
---|
112 | return; |
---|
113 | |
---|
114 | path[len] = '\0'; |
---|
115 | |
---|
116 | /* Find last slash */ |
---|
117 | int i; |
---|
118 | for (i = len - 1; (i >= 0) && (path[i] != '/'); i--); |
---|
119 | |
---|
120 | /* Do not include the slash */ |
---|
121 | i++; |
---|
122 | len -= i; |
---|
123 | |
---|
124 | /* Add space for trailing '\0' */ |
---|
125 | char *da_name = ofw_tree_space_alloc(len + 1); |
---|
126 | if (!da_name) |
---|
127 | return; |
---|
128 | |
---|
129 | memcpy(da_name, &path[i], len); |
---|
130 | da_name[len] = '\0'; |
---|
131 | current_node->da_name = (char *) balloc_rebase(da_name); |
---|
132 | |
---|
133 | /* |
---|
134 | * Recursively process the potential child node. |
---|
135 | */ |
---|
136 | phandle child = ofw_get_child_node(current); |
---|
137 | if ((child != 0) && (child != -1)) { |
---|
138 | ofw_tree_node_t *child_node = ofw_tree_node_alloc(); |
---|
139 | if (child_node) { |
---|
140 | ofw_tree_node_process(child_node, current_node, |
---|
141 | child); |
---|
142 | current_node->child = |
---|
143 | (ofw_tree_node_t *) balloc_rebase(child_node); |
---|
144 | } |
---|
145 | } |
---|
146 | |
---|
147 | /* |
---|
148 | * Count properties. |
---|
149 | */ |
---|
150 | static char name[OFW_TREE_PROPERTY_MAX_NAMELEN]; |
---|
151 | static char name2[OFW_TREE_PROPERTY_MAX_NAMELEN]; |
---|
152 | name[0] = '\0'; |
---|
153 | while (ofw_next_property(current, name, name2) == 1) { |
---|
154 | current_node->properties++; |
---|
155 | memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN); |
---|
156 | } |
---|
157 | |
---|
158 | if (!current_node->properties) |
---|
159 | return; |
---|
160 | |
---|
161 | /* |
---|
162 | * Copy properties. |
---|
163 | */ |
---|
164 | ofw_tree_property_t *property = |
---|
165 | ofw_tree_properties_alloc(current_node->properties); |
---|
166 | if (!property) |
---|
167 | return; |
---|
168 | |
---|
169 | name[0] = '\0'; |
---|
170 | for (i = 0; ofw_next_property(current, name, name2) == 1; i++) { |
---|
171 | if (i == current_node->properties) |
---|
172 | break; |
---|
173 | |
---|
174 | memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN); |
---|
175 | memcpy(property[i].name, name, OFW_TREE_PROPERTY_MAX_NAMELEN); |
---|
176 | property[i].name[OFW_TREE_PROPERTY_MAX_NAMELEN - 1] = '\0'; |
---|
177 | |
---|
178 | size_t size = ofw_get_proplen(current, name); |
---|
179 | property[i].size = size; |
---|
180 | |
---|
181 | if (size) { |
---|
182 | void *buf = ofw_tree_space_alloc(size); |
---|
183 | if (buf) { |
---|
184 | /* |
---|
185 | * Copy property value to memory node. |
---|
186 | */ |
---|
187 | (void) ofw_get_property(current, name, buf, size); |
---|
188 | property[i].value = balloc_rebase(buf); |
---|
189 | } |
---|
190 | } else |
---|
191 | property[i].value = NULL; |
---|
192 | } |
---|
193 | |
---|
194 | /* Just in case we ran out of memory. */ |
---|
195 | current_node->properties = i; |
---|
196 | current_node->property = (ofw_tree_property_t *) balloc_rebase(property); |
---|
197 | |
---|
198 | |
---|
199 | /* |
---|
200 | * Iteratively process the next peer node. |
---|
201 | * Note that recursion is a bad idea here. |
---|
202 | * Due to the topology of the OpenFirmware device tree, |
---|
203 | * the nesting of peer nodes could be to wide and the |
---|
204 | * risk of overflowing the stack is too real. |
---|
205 | */ |
---|
206 | phandle peer = ofw_get_peer_node(current); |
---|
207 | if ((peer != 0) && (peer != -1)) { |
---|
208 | ofw_tree_node_t *peer_node = ofw_tree_node_alloc(); |
---|
209 | if (peer_node) { |
---|
210 | current_node->peer = (ofw_tree_node_t *) balloc_rebase(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 | /* |
---|
221 | * No more peers on this level. |
---|
222 | */ |
---|
223 | break; |
---|
224 | } |
---|
225 | } |
---|
226 | |
---|
227 | /** Construct memory representation of OpenFirmware device tree. |
---|
228 | * |
---|
229 | * @return NULL on failure or kernel pointer to the root node. |
---|
230 | * |
---|
231 | */ |
---|
232 | ofw_tree_node_t *ofw_tree_build(void) |
---|
233 | { |
---|
234 | ofw_tree_node_t *root = ofw_tree_node_alloc(); |
---|
235 | if (root) |
---|
236 | ofw_tree_node_process(root, NULL, ofw_root); |
---|
237 | |
---|
238 | /* |
---|
239 | * The firmware client interface does not automatically include the |
---|
240 | * "ssm" node in the list of children of "/". A nasty yet working |
---|
241 | * solution is to explicitly stick "ssm" to the OFW tree. |
---|
242 | */ |
---|
243 | phandle ssm_node = ofw_find_device("/ssm@0,0"); |
---|
244 | if (ssm_node != -1) { |
---|
245 | ofw_tree_node_t *ssm = ofw_tree_node_alloc(); |
---|
246 | if (ssm) { |
---|
247 | ofw_tree_node_process(ssm, root, |
---|
248 | ofw_find_device("/ssm@0,0")); |
---|
249 | ssm->peer = root->child; |
---|
250 | root->child = (ofw_tree_node_t *) balloc_rebase(ssm); |
---|
251 | } |
---|
252 | } |
---|
253 | |
---|
254 | return (ofw_tree_node_t *) balloc_rebase(root); |
---|
255 | } |
---|