1 | /* |
2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | /* |
29 | * @OSF_FREE_COPYRIGHT@ |
30 | */ |
31 | |
32 | #include <pexpert/protos.h> |
33 | #include <pexpert/boot.h> |
34 | #include <pexpert/device_tree.h> |
35 | |
36 | #include <mach/mach_types.h> |
37 | #include <mach/machine/vm_types.h> |
38 | #include <kern/debug.h> |
39 | #include <kern/kern_types.h> |
40 | #include <kern/kalloc.h> |
41 | #include <os/overflow.h> |
42 | |
43 | #include <sys/types.h> |
44 | |
45 | static int DTInitialized; |
46 | static RealDTEntry DTRootNode; |
47 | |
48 | /* |
49 | * Support Routines |
50 | */ |
51 | static inline DeviceTreeNodeProperty* |
52 | next_prop(DeviceTreeNodeProperty* prop) |
53 | { |
54 | uintptr_t next_addr; |
55 | if (os_add3_overflow((uintptr_t)prop, prop->length, sizeof(DeviceTreeNodeProperty) + 3, &next_addr)) |
56 | panic("Device tree property overflow: prop %p, length 0x%x\n" , prop, prop->length); |
57 | next_addr &= ~(3ULL); |
58 | return (DeviceTreeNodeProperty*)next_addr; |
59 | } |
60 | |
61 | static RealDTEntry |
62 | skipProperties(RealDTEntry entry) |
63 | { |
64 | DeviceTreeNodeProperty *prop; |
65 | unsigned int k; |
66 | |
67 | if (entry == NULL || entry->nProperties == 0) { |
68 | return NULL; |
69 | } else { |
70 | prop = (DeviceTreeNodeProperty *) (entry + 1); |
71 | for (k = 0; k < entry->nProperties; k++) { |
72 | prop = next_prop(prop); |
73 | } |
74 | } |
75 | return ((RealDTEntry) prop); |
76 | } |
77 | |
78 | static RealDTEntry |
79 | skipTree(RealDTEntry root) |
80 | { |
81 | RealDTEntry entry; |
82 | unsigned int k; |
83 | |
84 | entry = skipProperties(root); |
85 | if (entry == NULL) { |
86 | return NULL; |
87 | } |
88 | for (k = 0; k < root->nChildren; k++) { |
89 | entry = skipTree(entry); |
90 | } |
91 | return entry; |
92 | } |
93 | |
94 | static RealDTEntry |
95 | GetFirstChild(RealDTEntry parent) |
96 | { |
97 | return skipProperties(parent); |
98 | } |
99 | |
100 | static RealDTEntry |
101 | GetNextChild(RealDTEntry sibling) |
102 | { |
103 | return skipTree(sibling); |
104 | } |
105 | |
106 | static const char * |
107 | GetNextComponent(const char *cp, char *bp) |
108 | { |
109 | size_t length = 0; |
110 | char *origbp = bp; |
111 | |
112 | while (*cp != 0) { |
113 | if (*cp == kDTPathNameSeparator) { |
114 | cp++; |
115 | break; |
116 | } |
117 | if (++length > kDTMaxEntryNameLength) { |
118 | *origbp = '\0'; |
119 | return cp; |
120 | } |
121 | *bp++ = *cp++; |
122 | } |
123 | *bp = 0; |
124 | return cp; |
125 | } |
126 | |
127 | static RealDTEntry |
128 | FindChild(RealDTEntry cur, char *buf) |
129 | { |
130 | RealDTEntry child; |
131 | unsigned long index; |
132 | char * str; |
133 | unsigned int dummy; |
134 | |
135 | if (cur->nChildren == 0) { |
136 | return NULL; |
137 | } |
138 | index = 1; |
139 | child = GetFirstChild(cur); |
140 | while (1) { |
141 | if (DTGetProperty(child, "name" , (void **)&str, &dummy) != kSuccess) { |
142 | break; |
143 | } |
144 | if (strcmp(str, buf) == 0) { |
145 | return child; |
146 | } |
147 | if (index >= cur->nChildren) { |
148 | break; |
149 | } |
150 | child = GetNextChild(child); |
151 | index++; |
152 | } |
153 | return NULL; |
154 | } |
155 | |
156 | |
157 | /* |
158 | * External Routines |
159 | */ |
160 | void |
161 | DTInit(void *base) |
162 | { |
163 | DTRootNode = (RealDTEntry) base; |
164 | DTInitialized = (DTRootNode != 0); |
165 | } |
166 | |
167 | int |
168 | DTEntryIsEqual(const DTEntry ref1, const DTEntry ref2) |
169 | { |
170 | /* equality of pointers */ |
171 | return (ref1 == ref2); |
172 | } |
173 | |
174 | static char *startingP; // needed for find_entry |
175 | int find_entry(const char *propName, const char *propValue, DTEntry *entryH); |
176 | |
177 | int DTFindEntry(const char *propName, const char *propValue, DTEntry *entryH) |
178 | { |
179 | if (!DTInitialized) { |
180 | return kError; |
181 | } |
182 | |
183 | startingP = (char *)DTRootNode; |
184 | return(find_entry(propName, propValue, entryH)); |
185 | } |
186 | |
187 | int find_entry(const char *propName, const char *propValue, DTEntry *entryH) |
188 | { |
189 | DeviceTreeNode *nodeP = (DeviceTreeNode *) (void *) startingP; |
190 | unsigned int k; |
191 | |
192 | if (nodeP->nProperties == 0) return(kError); // End of the list of nodes |
193 | startingP = (char *) (nodeP + 1); |
194 | |
195 | // Search current entry |
196 | for (k = 0; k < nodeP->nProperties; ++k) { |
197 | DeviceTreeNodeProperty *propP = (DeviceTreeNodeProperty *) (void *) startingP; |
198 | |
199 | startingP += sizeof (*propP) + ((propP->length + 3) & -4); |
200 | |
201 | if (strcmp (propP->name, propName) == 0) { |
202 | if (propValue == NULL || strcmp( (char *)(propP + 1), propValue) == 0) |
203 | { |
204 | *entryH = (DTEntry)nodeP; |
205 | return(kSuccess); |
206 | } |
207 | } |
208 | } |
209 | |
210 | // Search child nodes |
211 | for (k = 0; k < nodeP->nChildren; ++k) |
212 | { |
213 | if (find_entry(propName, propValue, entryH) == kSuccess) |
214 | return(kSuccess); |
215 | } |
216 | return(kError); |
217 | } |
218 | |
219 | int |
220 | DTLookupEntry(const DTEntry searchPoint, const char *pathName, DTEntry *foundEntry) |
221 | { |
222 | DTEntryNameBuf buf; |
223 | RealDTEntry cur; |
224 | const char * cp; |
225 | |
226 | if (!DTInitialized) { |
227 | return kError; |
228 | } |
229 | if (searchPoint == NULL) { |
230 | cur = DTRootNode; |
231 | } else { |
232 | cur = searchPoint; |
233 | } |
234 | cp = pathName; |
235 | if (*cp == kDTPathNameSeparator) { |
236 | cp++; |
237 | if (*cp == 0) { |
238 | *foundEntry = cur; |
239 | return kSuccess; |
240 | } |
241 | } |
242 | do { |
243 | cp = GetNextComponent(cp, buf); |
244 | |
245 | /* Check for done */ |
246 | if (*buf == 0) { |
247 | if (*cp == 0) { |
248 | *foundEntry = cur; |
249 | return kSuccess; |
250 | } |
251 | break; |
252 | } |
253 | |
254 | cur = FindChild(cur, buf); |
255 | |
256 | } while (cur != NULL); |
257 | |
258 | return kError; |
259 | } |
260 | |
261 | int |
262 | DTInitEntryIterator(const DTEntry startEntry, DTEntryIterator iter) |
263 | { |
264 | if (!DTInitialized) { |
265 | return kError; |
266 | } |
267 | |
268 | if (startEntry != NULL) { |
269 | iter->outerScope = (RealDTEntry) startEntry; |
270 | iter->currentScope = (RealDTEntry) startEntry; |
271 | } else { |
272 | iter->outerScope = DTRootNode; |
273 | iter->currentScope = DTRootNode; |
274 | } |
275 | iter->currentEntry = NULL; |
276 | iter->savedScope = NULL; |
277 | iter->currentIndex = 0; |
278 | |
279 | return kSuccess; |
280 | } |
281 | |
282 | int |
283 | DTEnterEntry(DTEntryIterator iter, DTEntry childEntry) |
284 | { |
285 | DTSavedScopePtr newScope; |
286 | |
287 | if (childEntry == NULL) { |
288 | return kError; |
289 | } |
290 | newScope = (DTSavedScopePtr) kalloc(sizeof(struct DTSavedScope)); |
291 | newScope->nextScope = iter->savedScope; |
292 | newScope->scope = iter->currentScope; |
293 | newScope->entry = iter->currentEntry; |
294 | newScope->index = iter->currentIndex; |
295 | |
296 | iter->currentScope = childEntry; |
297 | iter->currentEntry = NULL; |
298 | iter->savedScope = newScope; |
299 | iter->currentIndex = 0; |
300 | |
301 | return kSuccess; |
302 | } |
303 | |
304 | int |
305 | DTExitEntry(DTEntryIterator iter, DTEntry *currentPosition) |
306 | { |
307 | DTSavedScopePtr newScope; |
308 | |
309 | newScope = iter->savedScope; |
310 | if (newScope == NULL) { |
311 | return kError; |
312 | } |
313 | iter->savedScope = newScope->nextScope; |
314 | iter->currentScope = newScope->scope; |
315 | iter->currentEntry = newScope->entry; |
316 | iter->currentIndex = newScope->index; |
317 | *currentPosition = iter->currentEntry; |
318 | |
319 | kfree(newScope, sizeof(struct DTSavedScope)); |
320 | |
321 | return kSuccess; |
322 | } |
323 | |
324 | int |
325 | DTIterateEntries(DTEntryIterator iter, DTEntry *nextEntry) |
326 | { |
327 | if (iter->currentIndex >= iter->currentScope->nChildren) { |
328 | *nextEntry = NULL; |
329 | return kIterationDone; |
330 | } else { |
331 | iter->currentIndex++; |
332 | if (iter->currentIndex == 1) { |
333 | iter->currentEntry = GetFirstChild(iter->currentScope); |
334 | } else { |
335 | iter->currentEntry = GetNextChild(iter->currentEntry); |
336 | } |
337 | *nextEntry = iter->currentEntry; |
338 | return kSuccess; |
339 | } |
340 | } |
341 | |
342 | int |
343 | DTRestartEntryIteration(DTEntryIterator iter) |
344 | { |
345 | #if 0 |
346 | // This commented out code allows a second argument (outer) |
347 | // which (if true) causes restarting at the outer scope |
348 | // rather than the current scope. |
349 | DTSavedScopePtr scope; |
350 | |
351 | if (outer) { |
352 | while ((scope = iter->savedScope) != NULL) { |
353 | iter->savedScope = scope->nextScope; |
354 | kfree((vm_offset_t) scope, sizeof(struct DTSavedScope)); |
355 | } |
356 | iter->currentScope = iter->outerScope; |
357 | } |
358 | #endif |
359 | iter->currentEntry = NULL; |
360 | iter->currentIndex = 0; |
361 | return kSuccess; |
362 | } |
363 | |
364 | int |
365 | DTGetProperty(const DTEntry entry, const char *propertyName, void **propertyValue, unsigned int *propertySize) |
366 | { |
367 | DeviceTreeNodeProperty *prop; |
368 | unsigned int k; |
369 | |
370 | if (entry == NULL || entry->nProperties == 0) { |
371 | return kError; |
372 | } else { |
373 | prop = (DeviceTreeNodeProperty *) (entry + 1); |
374 | for (k = 0; k < entry->nProperties; k++) { |
375 | if (strcmp(prop->name, propertyName) == 0) { |
376 | *propertyValue = (void *) (((uintptr_t)prop) |
377 | + sizeof(DeviceTreeNodeProperty)); |
378 | *propertySize = prop->length; |
379 | return kSuccess; |
380 | } |
381 | prop = next_prop(prop); |
382 | } |
383 | } |
384 | return kError; |
385 | } |
386 | |
387 | int |
388 | DTInitPropertyIterator(const DTEntry entry, DTPropertyIterator iter) |
389 | { |
390 | |
391 | iter->entry = entry; |
392 | iter->currentProperty = NULL; |
393 | iter->currentIndex = 0; |
394 | return kSuccess; |
395 | } |
396 | |
397 | int |
398 | DTIterateProperties(DTPropertyIterator iter, char **foundProperty) |
399 | { |
400 | if (iter->currentIndex >= iter->entry->nProperties) { |
401 | *foundProperty = NULL; |
402 | return kIterationDone; |
403 | } else { |
404 | iter->currentIndex++; |
405 | if (iter->currentIndex == 1) { |
406 | iter->currentProperty = (DeviceTreeNodeProperty *) (iter->entry + 1); |
407 | } else { |
408 | iter->currentProperty = next_prop(iter->currentProperty); |
409 | } |
410 | *foundProperty = iter->currentProperty->name; |
411 | return kSuccess; |
412 | } |
413 | } |
414 | |
415 | int |
416 | DTRestartPropertyIteration(DTPropertyIterator iter) |
417 | { |
418 | iter->currentProperty = NULL; |
419 | iter->currentIndex = 0; |
420 | return kSuccess; |
421 | } |
422 | |
423 | |