2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * @OSF_FREE_COPYRIGHT@
32 #include <pexpert/protos.h>
33 #include <pexpert/boot.h>
34 #include <pexpert/device_tree.h>
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>
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) */
48 #include <sys/types.h>
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
;
62 assert_in_dt_region(vm_offset_t
const start
, vm_offset_t
const end
, void const *p
)
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
);
68 #define ASSERT_IN_DT(p) assert_in_dt_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (p))
71 assert_prop_in_dt_region(vm_offset_t
const start
, vm_offset_t
const end
, DeviceTreeNodeProperty
const *prop
)
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
);
79 assert_in_dt_region(start
, end
, (void*)prop_end
);
81 #define ASSERT_PROP_IN_DT(prop) assert_prop_in_dt_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (prop))
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))
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
92 * Internally, we check anything we want to access just before we want
93 * to access it (not after creating a pointer).
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
99 * Before returning a property value the caller, we check whether the
100 * property is fully within the region.
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.
108 static inline DeviceTreeNodeProperty
const *
109 next_prop_region(vm_offset_t
const start
, vm_offset_t end
, DeviceTreeNodeProperty
const *prop
)
113 ASSERT_HEADER_IN_DT_REGION(start
, end
, prop
, sizeof(DeviceTreeNode
));
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
);
119 next_addr
&= ~(3ULL);
121 return (DeviceTreeNodeProperty
*)next_addr
;
123 #define next_prop(prop) next_prop_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (prop))
126 skipProperties(RealDTEntry entry
)
128 DeviceTreeNodeProperty
const *prop
;
135 ASSERT_HEADER_IN_DT(entry
, sizeof(DeviceTreeNode
));
137 if (entry
->nProperties
== 0) {
140 prop
= (DeviceTreeNodeProperty
const *) (entry
+ 1);
141 for (k
= 0; k
< entry
->nProperties
; k
++) {
142 prop
= next_prop(prop
);
146 return (RealDTEntry
) prop
;
150 skipTree(RealDTEntry root
)
155 ASSERT_HEADER_IN_DT(root
, sizeof(DeviceTreeNode
));
157 entry
= skipProperties(root
);
161 for (k
= 0; k
< root
->nChildren
; k
++) {
162 entry
= skipTree(entry
);
168 GetFirstChild(RealDTEntry parent
)
170 return skipProperties(parent
);
174 GetNextChild(RealDTEntry sibling
)
176 return skipTree(sibling
);
180 GetNextComponent(const char *cp
, char *bp
)
186 if (*cp
== kDTPathNameSeparator
) {
190 if (++length
> kDTMaxEntryNameLength
) {
201 FindChild(RealDTEntry cur
, char *buf
)
208 ASSERT_HEADER_IN_DT(cur
, sizeof(DeviceTreeNode
));
210 if (cur
->nChildren
== 0) {
214 child
= GetFirstChild(cur
);
216 if (SecureDTGetProperty(child
, "name", (void const **)&str
, &dummy
) != kSuccess
) {
219 if (strcmp(str
, buf
) == 0) {
222 if (index
>= cur
->nChildren
) {
225 child
= GetNextChild(child
);
235 SecureDTInit(void const *base
, size_t size
)
237 if ((uintptr_t)base
+ size
< (uintptr_t)base
) {
238 panic("DeviceTree overflow: %p, size %#zx", base
, size
);
242 DTEnd
= (vm_offset_t
)DTRootNode
+ DTSize
;
243 DTInitialized
= (DTRootNode
!= 0);
247 SecureDTIsLockedDown(void)
249 #if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR)
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.
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.
259 addr64_t exec_header_phys
= kvtophys((vm_offset_t
)&_mh_execute_header
);
261 if (kvtophys((vm_offset_t
)DTRootNode
) < exec_header_phys
) {
262 assert(kvtophys(DTEnd
) < exec_header_phys
);
271 SecureDTEntryIsEqual(const DTEntry ref1
, const DTEntry ref2
)
273 /* equality of pointers */
277 static char const *startingP
; // needed for find_entry
278 int find_entry(const char *propName
, const char *propValue
, DTEntry
*entryH
);
281 SecureDTFindEntry(const char *propName
, const char *propValue
, DTEntry
*entryH
)
283 if (!DTInitialized
) {
287 startingP
= (char const *)DTRootNode
;
288 return find_entry(propName
, propValue
, entryH
);
292 find_entry(const char *propName
, const char *propValue
, DTEntry
*entryH
)
294 DeviceTreeNode
const *nodeP
= (DeviceTreeNode
const *) (void const *) startingP
;
297 ASSERT_HEADER_IN_DT(nodeP
, sizeof(DeviceTreeNode
));
299 if (nodeP
->nProperties
== 0) {
300 return kError
; // End of the list of nodes
302 startingP
= (char const *) (nodeP
+ 1);
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
);
309 startingP
+= sizeof(*propP
) + ((propP
->length
+ 3) & -4);
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
));
320 // Search child nodes
321 for (k
= 0; k
< nodeP
->nChildren
; ++k
) {
322 if (find_entry(propName
, propValue
, entryH
) == kSuccess
) {
330 SecureDTLookupEntry(const DTEntry searchPoint
, const char *pathName
, DTEntry
*foundEntry
)
336 if (!DTInitialized
) {
339 if (searchPoint
== NULL
) {
346 if (*cp
== kDTPathNameSeparator
) {
354 cp
= GetNextComponent(cp
, buf
);
365 cur
= FindChild(cur
, buf
);
366 } while (cur
!= NULL
);
372 SecureDTInitEntryIterator(const DTEntry startEntry
, DTEntryIterator iter
)
374 if (!DTInitialized
) {
378 if (startEntry
!= NULL
) {
379 iter
->outerScope
= (RealDTEntry
) startEntry
;
380 iter
->currentScope
= (RealDTEntry
) startEntry
;
382 iter
->outerScope
= DTRootNode
;
383 iter
->currentScope
= DTRootNode
;
385 iter
->currentEntry
= NULL
;
386 iter
->savedScope
= NULL
;
387 iter
->currentIndex
= 0;
393 SecureDTEnterEntry(DTEntryIterator iter
, DTEntry childEntry
)
395 DTSavedScopePtr newScope
;
397 if (childEntry
== NULL
) {
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
;
406 iter
->currentScope
= childEntry
;
407 iter
->currentEntry
= NULL
;
408 iter
->savedScope
= newScope
;
409 iter
->currentIndex
= 0;
415 SecureDTExitEntry(DTEntryIterator iter
, DTEntry
*currentPosition
)
417 DTSavedScopePtr newScope
;
419 newScope
= iter
->savedScope
;
420 if (newScope
== NULL
) {
423 iter
->savedScope
= newScope
->nextScope
;
424 iter
->currentScope
= newScope
->scope
;
425 iter
->currentEntry
= newScope
->entry
;
426 iter
->currentIndex
= newScope
->index
;
427 *currentPosition
= iter
->currentEntry
;
429 kfree(newScope
, sizeof(struct DTSavedScope
));
435 SecureDTIterateEntries(DTEntryIterator iter
, DTEntry
*nextEntry
)
437 if (iter
->currentIndex
>= iter
->currentScope
->nChildren
) {
439 return kIterationDone
;
441 iter
->currentIndex
++;
442 if (iter
->currentIndex
== 1) {
443 iter
->currentEntry
= GetFirstChild(iter
->currentScope
);
445 iter
->currentEntry
= GetNextChild(iter
->currentEntry
);
447 ASSERT_IN_DT(iter
->currentEntry
);
448 *nextEntry
= iter
->currentEntry
;
454 SecureDTRestartEntryIteration(DTEntryIterator iter
)
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
;
463 while ((scope
= iter
->savedScope
) != NULL
) {
464 iter
->savedScope
= scope
->nextScope
;
465 kfree((vm_offset_t
) scope
, sizeof(struct DTSavedScope
));
467 iter
->currentScope
= iter
->outerScope
;
470 iter
->currentEntry
= NULL
;
471 iter
->currentIndex
= 0;
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
)
478 DeviceTreeNodeProperty
const *prop
;
485 ASSERT_HEADER_IN_DT_REGION(region_start
, region_start
+ region_size
, entry
, sizeof(DeviceTreeNode
));
487 if (entry
->nProperties
== 0) {
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
;
499 prop
= next_prop_region(region_start
, region_start
+ region_size
, prop
);
506 SecureDTGetProperty(const DTEntry entry
, const char *propertyName
, void const **propertyValue
, unsigned int *propertySize
)
508 return SecureDTGetPropertyInternal(entry
, propertyName
, propertyValue
, propertySize
,
509 (vm_offset_t
)DTRootNode
, (vm_size_t
)((uintptr_t)DTEnd
- (uintptr_t)DTRootNode
));
512 #if defined(__i386__) || defined(__x86_64__)
514 SecureDTGetPropertyRegion(const DTEntry entry
, const char *propertyName
, void const **propertyValue
, unsigned int *propertySize
, vm_offset_t
const region_start
, vm_size_t region_size
)
516 return SecureDTGetPropertyInternal(entry
, propertyName
, propertyValue
, propertySize
,
517 region_start
, region_size
);
523 SecureDTInitPropertyIterator(const DTEntry entry
, DTPropertyIterator iter
)
526 iter
->currentProperty
= NULL
;
527 iter
->currentIndex
= 0;
532 SecureDTIterateProperties(DTPropertyIterator iter
, char const **foundProperty
)
534 if (iter
->currentIndex
>= iter
->entry
->nProperties
) {
535 *foundProperty
= NULL
;
536 return kIterationDone
;
538 iter
->currentIndex
++;
539 if (iter
->currentIndex
== 1) {
540 iter
->currentProperty
= (DeviceTreeNodeProperty
const *) (iter
->entry
+ 1);
542 iter
->currentProperty
= next_prop(iter
->currentProperty
);
544 ASSERT_PROP_IN_DT(iter
->currentProperty
);
545 *foundProperty
= iter
->currentProperty
->name
;
551 SecureDTRestartPropertyIteration(DTPropertyIterator iter
)
553 iter
->currentProperty
= NULL
;
554 iter
->currentIndex
= 0;