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