]>
Commit | Line | Data |
---|---|---|
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 <libkern/kernel_mach_header.h> | |
42 | #include <os/overflow.h> | |
43 | ||
44 | #if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) | |
45 | extern addr64_t kvtophys(vm_offset_t va); | |
46 | #endif /* defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) */ | |
47 | ||
48 | #include <sys/types.h> | |
49 | ||
50 | SECURITY_READ_ONLY_LATE(static int) DTInitialized; | |
51 | SECURITY_READ_ONLY_LATE(RealDTEntry) DTRootNode; | |
52 | SECURITY_READ_ONLY_LATE(static vm_size_t) DTSize; | |
53 | SECURITY_READ_ONLY_LATE(static vm_offset_t) DTEnd; | |
54 | ||
55 | /* | |
56 | * | |
57 | * Support Routines | |
58 | * | |
59 | */ | |
60 | ||
61 | static inline void | |
62 | assert_in_dt_region(vm_offset_t const start, vm_offset_t const end, void const *p) | |
63 | { | |
64 | if ((vm_offset_t)p < start || (vm_offset_t)p > end) { | |
65 | panic("Device tree pointer outside of device tree region: pointer %p, DTEnd %lx\n", p, (unsigned long)DTEnd); | |
66 | } | |
67 | } | |
68 | #define ASSERT_IN_DT(p) assert_in_dt_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (p)) | |
69 | ||
70 | static inline void | |
71 | assert_prop_in_dt_region(vm_offset_t const start, vm_offset_t const end, DeviceTreeNodeProperty const *prop) | |
72 | { | |
73 | vm_offset_t prop_end; | |
74 | ||
75 | assert_in_dt_region(start, end, prop); | |
76 | if (os_add3_overflow((vm_offset_t)prop, sizeof(DeviceTreeNodeProperty), prop->length, &prop_end)) { | |
77 | panic("Device tree property overflow: prop %p, length 0x%x\n", prop, prop->length); | |
78 | } | |
79 | assert_in_dt_region(start, end, (void*)prop_end); | |
80 | } | |
81 | #define ASSERT_PROP_IN_DT(prop) assert_prop_in_dt_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (prop)) | |
82 | ||
83 | #define ASSERT_HEADER_IN_DT_REGION(start, end, p, size) assert_in_dt_region((start), (end), (uint8_t const *)(p) + (size)) | |
84 | #define ASSERT_HEADER_IN_DT(p, size) ASSERT_IN_DT((uint8_t const *)(p) + (size)) | |
85 | ||
86 | /* | |
87 | * Since there is no way to know the size of a device tree node | |
88 | * without fully walking it, we employ the following principle to make | |
89 | * sure that the accessed device tree is fully within its memory | |
90 | * region: | |
91 | * | |
92 | * Internally, we check anything we want to access just before we want | |
93 | * to access it (not after creating a pointer). | |
94 | * | |
95 | * Then, before returning a DTEntry to the caller, we check whether | |
96 | * the start address (only!) of the entry is still within the device | |
97 | * tree region. | |
98 | * | |
99 | * Before returning a property value the caller, we check whether the | |
100 | * property is fully within the region. | |
101 | * | |
102 | * "DTEntry"s are opaque to the caller, so only checking their | |
103 | * starting address is enough to satisfy existence within the device | |
104 | * tree region, while for property values we need to make sure that | |
105 | * they are fully within the region. | |
106 | */ | |
107 | ||
108 | static inline DeviceTreeNodeProperty const * | |
109 | next_prop_region(vm_offset_t const start, vm_offset_t end, DeviceTreeNodeProperty const *prop) | |
110 | { | |
111 | uintptr_t next_addr; | |
112 | ||
113 | ASSERT_HEADER_IN_DT_REGION(start, end, prop, sizeof(DeviceTreeNode)); | |
114 | ||
115 | if (os_add3_overflow((uintptr_t)prop, prop->length, sizeof(DeviceTreeNodeProperty) + 3, &next_addr)) { | |
116 | panic("Device tree property overflow: prop %p, length 0x%x\n", prop, prop->length); | |
117 | } | |
118 | ||
119 | next_addr &= ~(3ULL); | |
120 | ||
121 | return (DeviceTreeNodeProperty*)next_addr; | |
122 | } | |
123 | #define next_prop(prop) next_prop_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (prop)) | |
124 | ||
125 | static RealDTEntry | |
126 | skipProperties(RealDTEntry entry) | |
127 | { | |
128 | DeviceTreeNodeProperty const *prop; | |
129 | unsigned int k; | |
130 | ||
131 | if (entry == NULL) { | |
132 | return NULL; | |
133 | } | |
134 | ||
135 | ASSERT_HEADER_IN_DT(entry, sizeof(DeviceTreeNode)); | |
136 | ||
137 | if (entry->nProperties == 0) { | |
138 | return NULL; | |
139 | } else { | |
140 | prop = (DeviceTreeNodeProperty const *) (entry + 1); | |
141 | for (k = 0; k < entry->nProperties; k++) { | |
142 | prop = next_prop(prop); | |
143 | } | |
144 | } | |
145 | ASSERT_IN_DT(prop); | |
146 | return (RealDTEntry) prop; | |
147 | } | |
148 | ||
149 | static RealDTEntry | |
150 | skipTree(RealDTEntry root) | |
151 | { | |
152 | RealDTEntry entry; | |
153 | unsigned int k; | |
154 | ||
155 | ASSERT_HEADER_IN_DT(root, sizeof(DeviceTreeNode)); | |
156 | ||
157 | entry = skipProperties(root); | |
158 | if (entry == NULL) { | |
159 | return NULL; | |
160 | } | |
161 | for (k = 0; k < root->nChildren; k++) { | |
162 | entry = skipTree(entry); | |
163 | } | |
164 | return entry; | |
165 | } | |
166 | ||
167 | static RealDTEntry | |
168 | GetFirstChild(RealDTEntry parent) | |
169 | { | |
170 | return skipProperties(parent); | |
171 | } | |
172 | ||
173 | static RealDTEntry | |
174 | GetNextChild(RealDTEntry sibling) | |
175 | { | |
176 | return skipTree(sibling); | |
177 | } | |
178 | ||
179 | static const char * | |
180 | GetNextComponent(const char *cp, char *bp) | |
181 | { | |
182 | size_t length = 0; | |
183 | char *origbp = bp; | |
184 | ||
185 | while (*cp != 0) { | |
186 | if (*cp == kDTPathNameSeparator) { | |
187 | cp++; | |
188 | break; | |
189 | } | |
190 | if (++length > kDTMaxEntryNameLength) { | |
191 | *origbp = '\0'; | |
192 | return cp; | |
193 | } | |
194 | *bp++ = *cp++; | |
195 | } | |
196 | *bp = 0; | |
197 | return cp; | |
198 | } | |
199 | ||
200 | static RealDTEntry | |
201 | FindChild(RealDTEntry cur, char *buf) | |
202 | { | |
203 | RealDTEntry child; | |
204 | unsigned long index; | |
205 | char const * str; | |
206 | unsigned int dummy; | |
207 | ||
208 | ASSERT_HEADER_IN_DT(cur, sizeof(DeviceTreeNode)); | |
209 | ||
210 | if (cur->nChildren == 0) { | |
211 | return NULL; | |
212 | } | |
213 | index = 1; | |
214 | child = GetFirstChild(cur); | |
215 | while (1) { | |
216 | if (SecureDTGetProperty(child, "name", (void const **)&str, &dummy) != kSuccess) { | |
217 | break; | |
218 | } | |
219 | if (strcmp(str, buf) == 0) { | |
220 | return child; | |
221 | } | |
222 | if (index >= cur->nChildren) { | |
223 | break; | |
224 | } | |
225 | child = GetNextChild(child); | |
226 | index++; | |
227 | } | |
228 | return NULL; | |
229 | } | |
230 | ||
231 | /* | |
232 | * External Routines | |
233 | */ | |
234 | void | |
235 | SecureDTInit(void const *base, size_t size) | |
236 | { | |
237 | if ((uintptr_t)base + size < (uintptr_t)base) { | |
238 | panic("DeviceTree overflow: %p, size %#zx", base, size); | |
239 | } | |
240 | DTRootNode = base; | |
241 | DTSize = size; | |
242 | DTEnd = (vm_offset_t)DTRootNode + DTSize; | |
243 | DTInitialized = (DTRootNode != 0); | |
244 | } | |
245 | ||
246 | bool | |
247 | SecureDTIsLockedDown(void) | |
248 | { | |
249 | #if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) | |
250 | /* | |
251 | * We cannot check if the DT is in the CTRR region early on, | |
252 | * because knowledge of the CTRR region is set up later. But the | |
253 | * DT is used in all kinds of early bootstrapping before that. | |
254 | * | |
255 | * Luckily, we know that the device tree must be in front of the | |
256 | * kernel if set up in EXTRADATA (which means it's covered by | |
257 | * CTRR), and after it otherwise. | |
258 | */ | |
259 | addr64_t exec_header_phys = kvtophys((vm_offset_t)&_mh_execute_header); | |
260 | ||
261 | if (kvtophys((vm_offset_t)DTRootNode) < exec_header_phys) { | |
262 | assert(kvtophys(DTEnd) < exec_header_phys); | |
263 | return true; | |
264 | } | |
265 | ||
266 | #endif | |
267 | return false; | |
268 | } | |
269 | ||
270 | int | |
271 | SecureDTEntryIsEqual(const DTEntry ref1, const DTEntry ref2) | |
272 | { | |
273 | /* equality of pointers */ | |
274 | return ref1 == ref2; | |
275 | } | |
276 | ||
277 | static char const *startingP; // needed for find_entry | |
278 | int find_entry(const char *propName, const char *propValue, DTEntry *entryH); | |
279 | ||
280 | int | |
281 | SecureDTFindEntry(const char *propName, const char *propValue, DTEntry *entryH) | |
282 | { | |
283 | if (!DTInitialized) { | |
284 | return kError; | |
285 | } | |
286 | ||
287 | startingP = (char const *)DTRootNode; | |
288 | return find_entry(propName, propValue, entryH); | |
289 | } | |
290 | ||
291 | int | |
292 | find_entry(const char *propName, const char *propValue, DTEntry *entryH) | |
293 | { | |
294 | DeviceTreeNode const *nodeP = (DeviceTreeNode const *) (void const *) startingP; | |
295 | unsigned int k; | |
296 | ||
297 | ASSERT_HEADER_IN_DT(nodeP, sizeof(DeviceTreeNode)); | |
298 | ||
299 | if (nodeP->nProperties == 0) { | |
300 | return kError; // End of the list of nodes | |
301 | } | |
302 | startingP = (char const *) (nodeP + 1); | |
303 | ||
304 | // Search current entry | |
305 | for (k = 0; k < nodeP->nProperties; ++k) { | |
306 | DeviceTreeNodeProperty const *propP = (DeviceTreeNodeProperty const *) (void const *) startingP; | |
307 | ASSERT_PROP_IN_DT(propP); | |
308 | ||
309 | startingP += sizeof(*propP) + ((propP->length + 3) & -4); | |
310 | ||
311 | if (strcmp(propP->name, propName) == 0) { | |
312 | if (propValue == NULL || strcmp((char const *)(propP + 1), propValue) == 0) { | |
313 | *entryH = (DTEntry)nodeP; | |
314 | ASSERT_HEADER_IN_DT(*entryH, sizeof(DeviceTreeNode)); | |
315 | return kSuccess; | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | // Search child nodes | |
321 | for (k = 0; k < nodeP->nChildren; ++k) { | |
322 | if (find_entry(propName, propValue, entryH) == kSuccess) { | |
323 | return kSuccess; | |
324 | } | |
325 | } | |
326 | return kError; | |
327 | } | |
328 | ||
329 | int | |
330 | SecureDTLookupEntry(const DTEntry searchPoint, const char *pathName, DTEntry *foundEntry) | |
331 | { | |
332 | DTEntryNameBuf buf; | |
333 | RealDTEntry cur; | |
334 | const char * cp; | |
335 | ||
336 | if (!DTInitialized) { | |
337 | return kError; | |
338 | } | |
339 | if (searchPoint == NULL) { | |
340 | cur = DTRootNode; | |
341 | } else { | |
342 | cur = searchPoint; | |
343 | } | |
344 | ASSERT_IN_DT(cur); | |
345 | cp = pathName; | |
346 | if (*cp == kDTPathNameSeparator) { | |
347 | cp++; | |
348 | if (*cp == 0) { | |
349 | *foundEntry = cur; | |
350 | return kSuccess; | |
351 | } | |
352 | } | |
353 | do { | |
354 | cp = GetNextComponent(cp, buf); | |
355 | ||
356 | /* Check for done */ | |
357 | if (*buf == 0) { | |
358 | if (*cp == 0) { | |
359 | *foundEntry = cur; | |
360 | return kSuccess; | |
361 | } | |
362 | break; | |
363 | } | |
364 | ||
365 | cur = FindChild(cur, buf); | |
366 | } while (cur != NULL); | |
367 | ||
368 | return kError; | |
369 | } | |
370 | ||
371 | int | |
372 | SecureDTInitEntryIterator(const DTEntry startEntry, DTEntryIterator iter) | |
373 | { | |
374 | if (!DTInitialized) { | |
375 | return kError; | |
376 | } | |
377 | ||
378 | if (startEntry != NULL) { | |
379 | iter->outerScope = (RealDTEntry) startEntry; | |
380 | iter->currentScope = (RealDTEntry) startEntry; | |
381 | } else { | |
382 | iter->outerScope = DTRootNode; | |
383 | iter->currentScope = DTRootNode; | |
384 | } | |
385 | iter->currentEntry = NULL; | |
386 | iter->savedScope = NULL; | |
387 | iter->currentIndex = 0; | |
388 | ||
389 | return kSuccess; | |
390 | } | |
391 | ||
392 | int | |
393 | SecureDTEnterEntry(DTEntryIterator iter, DTEntry childEntry) | |
394 | { | |
395 | DTSavedScopePtr newScope; | |
396 | ||
397 | if (childEntry == NULL) { | |
398 | return kError; | |
399 | } | |
400 | newScope = (DTSavedScopePtr) kalloc(sizeof(struct DTSavedScope)); | |
401 | newScope->nextScope = iter->savedScope; | |
402 | newScope->scope = iter->currentScope; | |
403 | newScope->entry = iter->currentEntry; | |
404 | newScope->index = iter->currentIndex; | |
405 | ||
406 | iter->currentScope = childEntry; | |
407 | iter->currentEntry = NULL; | |
408 | iter->savedScope = newScope; | |
409 | iter->currentIndex = 0; | |
410 | ||
411 | return kSuccess; | |
412 | } | |
413 | ||
414 | int | |
415 | SecureDTExitEntry(DTEntryIterator iter, DTEntry *currentPosition) | |
416 | { | |
417 | DTSavedScopePtr newScope; | |
418 | ||
419 | newScope = iter->savedScope; | |
420 | if (newScope == NULL) { | |
421 | return kError; | |
422 | } | |
423 | iter->savedScope = newScope->nextScope; | |
424 | iter->currentScope = newScope->scope; | |
425 | iter->currentEntry = newScope->entry; | |
426 | iter->currentIndex = newScope->index; | |
427 | *currentPosition = iter->currentEntry; | |
428 | ||
429 | kfree(newScope, sizeof(struct DTSavedScope)); | |
430 | ||
431 | return kSuccess; | |
432 | } | |
433 | ||
434 | int | |
435 | SecureDTIterateEntries(DTEntryIterator iter, DTEntry *nextEntry) | |
436 | { | |
437 | if (iter->currentIndex >= iter->currentScope->nChildren) { | |
438 | *nextEntry = NULL; | |
439 | return kIterationDone; | |
440 | } else { | |
441 | iter->currentIndex++; | |
442 | if (iter->currentIndex == 1) { | |
443 | iter->currentEntry = GetFirstChild(iter->currentScope); | |
444 | } else { | |
445 | iter->currentEntry = GetNextChild(iter->currentEntry); | |
446 | } | |
447 | ASSERT_IN_DT(iter->currentEntry); | |
448 | *nextEntry = iter->currentEntry; | |
449 | return kSuccess; | |
450 | } | |
451 | } | |
452 | ||
453 | int | |
454 | SecureDTRestartEntryIteration(DTEntryIterator iter) | |
455 | { | |
456 | #if 0 | |
457 | // This commented out code allows a second argument (outer) | |
458 | // which (if true) causes restarting at the outer scope | |
459 | // rather than the current scope. | |
460 | DTSavedScopePtr scope; | |
461 | ||
462 | if (outer) { | |
463 | while ((scope = iter->savedScope) != NULL) { | |
464 | iter->savedScope = scope->nextScope; | |
465 | kfree((vm_offset_t) scope, sizeof(struct DTSavedScope)); | |
466 | } | |
467 | iter->currentScope = iter->outerScope; | |
468 | } | |
469 | #endif | |
470 | iter->currentEntry = NULL; | |
471 | iter->currentIndex = 0; | |
472 | return kSuccess; | |
473 | } | |
474 | ||
475 | static int | |
476 | SecureDTGetPropertyInternal(const DTEntry entry, const char *propertyName, void const **propertyValue, unsigned int *propertySize, vm_offset_t const region_start, vm_size_t region_size) | |
477 | { | |
478 | DeviceTreeNodeProperty const *prop; | |
479 | unsigned int k; | |
480 | ||
481 | if (entry == NULL) { | |
482 | return kError; | |
483 | } | |
484 | ||
485 | ASSERT_HEADER_IN_DT_REGION(region_start, region_start + region_size, entry, sizeof(DeviceTreeNode)); | |
486 | ||
487 | if (entry->nProperties == 0) { | |
488 | return kError; | |
489 | } else { | |
490 | prop = (DeviceTreeNodeProperty const *) (entry + 1); | |
491 | for (k = 0; k < entry->nProperties; k++) { | |
492 | assert_prop_in_dt_region(region_start, region_start + region_size, prop); | |
493 | if (strcmp(prop->name, propertyName) == 0) { | |
494 | *propertyValue = (void const *) (((uintptr_t)prop) | |
495 | + sizeof(DeviceTreeNodeProperty)); | |
496 | *propertySize = prop->length; | |
497 | return kSuccess; | |
498 | } | |
499 | prop = next_prop_region(region_start, region_start + region_size, prop); | |
500 | } | |
501 | } | |
502 | return kError; | |
503 | } | |
504 | ||
505 | int | |
506 | SecureDTGetProperty(const DTEntry entry, const char *propertyName, void const **propertyValue, unsigned int *propertySize) | |
507 | { | |
508 | return SecureDTGetPropertyInternal(entry, propertyName, propertyValue, propertySize, | |
509 | (vm_offset_t)DTRootNode, (vm_size_t)((uintptr_t)DTEnd - (uintptr_t)DTRootNode)); | |
510 | } | |
511 | ||
512 | int | |
513 | SecureDTGetPropertyRegion(const DTEntry entry, const char *propertyName, void const **propertyValue, unsigned int *propertySize, vm_offset_t const region_start, vm_size_t region_size) | |
514 | { | |
515 | return SecureDTGetPropertyInternal(entry, propertyName, propertyValue, propertySize, | |
516 | region_start, region_size); | |
517 | } | |
518 | ||
519 | ||
520 | int | |
521 | SecureDTInitPropertyIterator(const DTEntry entry, DTPropertyIterator iter) | |
522 | { | |
523 | iter->entry = entry; | |
524 | iter->currentProperty = NULL; | |
525 | iter->currentIndex = 0; | |
526 | return kSuccess; | |
527 | } | |
528 | ||
529 | int | |
530 | SecureDTIterateProperties(DTPropertyIterator iter, char const **foundProperty) | |
531 | { | |
532 | if (iter->currentIndex >= iter->entry->nProperties) { | |
533 | *foundProperty = NULL; | |
534 | return kIterationDone; | |
535 | } else { | |
536 | iter->currentIndex++; | |
537 | if (iter->currentIndex == 1) { | |
538 | iter->currentProperty = (DeviceTreeNodeProperty const *) (iter->entry + 1); | |
539 | } else { | |
540 | iter->currentProperty = next_prop(iter->currentProperty); | |
541 | } | |
542 | ASSERT_PROP_IN_DT(iter->currentProperty); | |
543 | *foundProperty = iter->currentProperty->name; | |
544 | return kSuccess; | |
545 | } | |
546 | } | |
547 | ||
548 | int | |
549 | SecureDTRestartPropertyIteration(DTPropertyIterator iter) | |
550 | { | |
551 | iter->currentProperty = NULL; | |
552 | iter->currentIndex = 0; | |
553 | return kSuccess; | |
554 | } |