xref: /xnu-11215/iokit/Kernel/IONVRAM.cpp (revision bb611c8f)
1 /*
2  * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
3  * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
4  *
5  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6  *
7  * This file contains Original Code and/or Modifications of Original Code
8  * as defined in and that are subject to the Apple Public Source License
9  * Version 2.0 (the 'License'). You may not use this file except in
10  * compliance with the License. The rights granted to you under the License
11  * may not be used to create, or enable the creation or redistribution of,
12  * unlawful or unlicensed copies of an Apple operating system, or to
13  * circumvent, violate, or enable the circumvention or violation of, any
14  * terms of an Apple operating system software license agreement.
15  *
16  * Please obtain a copy of the License at
17  * http://www.opensource.apple.com/apsl/ and read it before using this file.
18  *
19  * The Original Code and all software distributed under the License are
20  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
21  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
22  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
24  * Please see the License for the specific language governing rights and
25  * limitations under the License.
26  *
27  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28  */
29 
30 #define IOKIT_ENABLE_SHARED_PTR
31 
32 #include <AssertMacros.h>
33 #include <IOKit/IOLib.h>
34 #include <IOKit/IONVRAM.h>
35 #include <IOKit/IOPlatformExpert.h>
36 #include <IOKit/IOUserClient.h>
37 #include <IOKit/IOKitKeys.h>
38 #include <IOKit/IOKitKeysPrivate.h>
39 #include <IOKit/IOBSD.h>
40 #include <kern/debug.h>
41 #include <pexpert/boot.h>
42 #include <pexpert/pexpert.h>
43 
44 #define super IOService
45 
46 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
47 
48 // Internal values
49 #define NVRAM_CHRP_SIG_APPLE             0x5A
50 #define NVRAM_CHRP_APPLE_HEADER_NAME     "nvram"
51 
52 // From Apple CHRP Spec
53 #define NVRAM_CHRP_SIG_SYSTEM    0x70
54 #define NVRAM_CHRP_SIG_CONFIG    0x71
55 #define NVRAM_CHRP_SIG_FREESPACE 0x7F
56 
57 #define NVRAM_CHRP_PARTITION_NAME_COMMON        "common"
58 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM        "system"
59 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY "secure"
60 #define NVRAM_CHRP_PARTITION_NAME_FREESPACE     "\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77"
61 
62 #define NVRAM_CHRP_LENGTH_BLOCK_SIZE 0x10 // CHRP length field is in 16 byte blocks
63 
64 typedef struct chrp_nvram_header { //16 bytes
65 	uint8_t  sig;
66 	uint8_t  cksum; // checksum on sig, len, and name
67 	uint16_t len;   // total length of the partition in 16 byte blocks starting with the signature
68 	// and ending with the last byte of data area, ie len includes its own header size
69 	char     name[12];
70 	uint8_t  data[0];
71 } chrp_nvram_header_t;
72 
73 typedef struct apple_nvram_header {  // 16 + 16 bytes
74 	struct   chrp_nvram_header chrp;
75 	uint32_t adler;
76 	uint32_t generation;
77 	uint8_t  padding[8];
78 } apple_nvram_header_t;
79 
80 
81 #define kIONVRAMPrivilege       kIOClientPrivilegeAdministrator
82 
83 OSDefineMetaClassAndStructors(IODTNVRAM, IOService);
84 
85 #if defined(DEBUG) || defined(DEVELOPMENT)
86 #define DEBUG_INFO(fmt, args...)                                    \
87 ({                                                                  \
88 	if (gNVRAMLogging)                                              \
89 	IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \
90 })
91 
92 #define DEBUG_ALWAYS(fmt, args...)                                  \
93 ({                                                                  \
94 	IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \
95 })
96 #else
97 #define DEBUG_INFO(fmt, args...)
98 #define DEBUG_ALWAYS(fmt, args...)
99 #endif
100 
101 #define DEBUG_ERROR DEBUG_ALWAYS
102 
103 #define NVRAMLOCK()                              \
104 ({                                               \
105 	if (preemption_enabled() && !panic_active()) \
106 	        IOLockLock(_variableLock);           \
107 })
108 
109 #define NVRAMUNLOCK()                            \
110 ({                                               \
111 	if (preemption_enabled() && !panic_active()) \
112 	        IOLockUnlock(_variableLock);         \
113 })
114 
115 #define NVRAMLOCKASSERT()                                    \
116 ({                                                           \
117 	if (preemption_enabled() && !panic_active())             \
118 	        IOLockAssert(_variableLock, kIOLockAssertOwned); \
119 })
120 
121 typedef struct {
122 	const char                *name;
123 	UInt32                    offset;
124 	UInt32                    size;
125 	OSSharedPtr<OSDictionary> &dict;
126 	UInt8                     *image;
127 } NVRAMRegionInfo;
128 
129 // Guid for Apple System Boot variables
130 // 40A0DDD2-77F8-4392-B4A3-1E7304206516
131 UUID_DEFINE(gAppleSystemVariableGuid, 0x40, 0xA0, 0xDD, 0xD2, 0x77, 0xF8, 0x43, 0x92, 0xB4, 0xA3, 0x1E, 0x73, 0x04, 0x20, 0x65, 0x16);
132 
133 // Apple NVRAM Variable namespace (APPLE_VENDOR_OS_VARIABLE_GUID)
134 // 7C436110-AB2A-4BBB-A880-FE41995C9F82
135 UUID_DEFINE(gAppleNVRAMGuid, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82);
136 
137 static bool gNVRAMLogging = false;
138 
139 // allowlist variables from macboot that need to be set/get from system region if present
140 static const char * const gNVRAMSystemList[] = {
141 	"adbe-tunable",
142 	"adbe-tunables",
143 	"adfe-tunables",
144 	"alamo-path",
145 	"alt-boot-volume",
146 	"ASMB",
147 	"atc0",
148 	"atc1",
149 	"auto-boot",
150 	"auto-boot-halt-stage",
151 	"auto-boot-once",
152 	"auto-boot-usb",
153 	"auxkc-path",
154 	"backlight-level",
155 	"backlight-nits",
156 	"base-system-path",
157 	"boot-args",
158 	"boot-breadcrumbs",
159 	"boot-command",
160 	"boot-device",
161 	"boot-image",
162 	"boot-partition",
163 	"boot-path",
164 	"boot-ramdisk",
165 	"boot-script",
166 	"boot-volume",
167 	"bootdelay",
168 	"bt1addr",
169 	"btaddr",
170 	"cam-use-ext-ldo",
171 	"CLCG_override",
172 	"com.apple.System.boot-nonce",
173 	"com.apple.System.rtc-offset",
174 	"com.apple.System.tz0-size",
175 	"core-bin-offset",
176 	"cpu-bin-offset",
177 	"darkboot",
178 	"DClr_override",
179 	"dcp-auto-boot",
180 	"debug-gg",
181 	"debug-soc",
182 	"debug-uarts",
183 	"diags-path",
184 	"disable-boot-wdt",
185 	"display-color-space",
186 	"display-timing",
187 	"display-vsh-comp",
188 	"dpcd-max-brightness",
189 	"dtdump",
190 	"dtdump-path",
191 	"e75",
192 	"emu",
193 	"enable-auth-debug",
194 	"enable-jop",
195 	"enable-marconi",
196 	"enable-upgrade-fallback",
197 	"enforce-iuob",
198 	"eth1addr",
199 	"ethaddr",
200 	"failboot-breadcrumbs",
201 	"fixed-lcm-boost",
202 	"force-ctrr-lock",
203 	"force-upgrade-fail",
204 	"fuos-path",
205 	"hib-ui-force",
206 	"hibhack-test-hmac",
207 	"iboot-data",
208 	"iboot-failure-reason",
209 	"iboot-failure-reason-str",
210 	"iboot-failure-volume",
211 	"iboot1-precommitted",
212 	"idle-off",
213 	"is-tethered",
214 	"kaslr-off",
215 	"kaslr-slide",
216 	"kis-rsm",
217 	"knobs",
218 	"loadaddr",
219 	"memmapdump",
220 	"mipi-bridge-cmd-verify",
221 	"mipi-bridge-poll-cmd-fifo",
222 	"no-ctrr",
223 	"one-time-boot-command",
224 	"osenvironment",
225 	"ota-breadcrumbs",
226 	"ota-outcome",
227 	"panicmedic",
228 	"panicmedic-threshold",
229 	"panicmedic-timestamps",
230 	"phleet-path",
231 	"pinot-panel-id",
232 	"pintoaddr",
233 	"policy-nonce-digests",
234 	"preserve-debuggability",
235 	"prevent-restores", // Keep for factory <rdar://problem/70476321>
236 	"prev-lang:kbd",
237 	"ramrod-kickstart-aces",
238 	"rbdaddr0",
239 	"rbm-path",
240 	"reconfig-behavior",
241 	"reconfig-breakpoints",
242 	"recovery-boot-mode",
243 	"recovery-breadcrumbs",
244 	"restored-host-timeout",
245 	"root-live-fs",
246 	"rtos-path",
247 	"soc-bin-offset",
248 	"StartupMute",
249 	"StartupMuteAccessibility",
250 	"storage-prev-assert",
251 	"storage-prev-assert-stored",
252 	"summit-panel-id",
253 	"SystemAudioVolume",
254 	"SystemAudioVolumeExtension",
255 	"SystemAudioVolumeSaved",
256 	"tz0-size-override",
257 	"upgrade-fallback-boot-command",
258 	"upgrade-retry",
259 	"usb-enabled",
260 	"wifi1addr",
261 	"wifiaddr",
262 	nullptr
263 };
264 
265 typedef struct {
266 	const char *name;
267 	IONVRAMVariableType type;
268 } VariableTypeEntry;
269 
270 static const
271 VariableTypeEntry gVariableTypes[] = {
272 	{"auto-boot?", kOFVariableTypeBoolean},
273 	{"boot-args", kOFVariableTypeString},
274 	{"boot-command", kOFVariableTypeString},
275 	{"boot-device", kOFVariableTypeString},
276 	{"boot-file", kOFVariableTypeString},
277 	{"boot-screen", kOFVariableTypeString},
278 	{"boot-script", kOFVariableTypeString},
279 	{"console-screen", kOFVariableTypeString},
280 	{"default-client-ip", kOFVariableTypeString},
281 	{"default-gateway-ip", kOFVariableTypeString},
282 	{"default-mac-address?", kOFVariableTypeBoolean},
283 	{"default-router-ip", kOFVariableTypeString},
284 	{"default-server-ip", kOFVariableTypeString},
285 	{"default-subnet-mask", kOFVariableTypeString},
286 	{"diag-device", kOFVariableTypeString},
287 	{"diag-file", kOFVariableTypeString},
288 	{"diag-switch?", kOFVariableTypeBoolean},
289 	{"fcode-debug?", kOFVariableTypeBoolean},
290 	{"input-device", kOFVariableTypeString},
291 	{"input-device-1", kOFVariableTypeString},
292 	{"little-endian?", kOFVariableTypeBoolean},
293 	{"load-base", kOFVariableTypeNumber},
294 	{"mouse-device", kOFVariableTypeString},
295 	{"nvramrc", kOFVariableTypeString},
296 	{"oem-banner", kOFVariableTypeString},
297 	{"oem-banner?", kOFVariableTypeBoolean},
298 	{"oem-logo", kOFVariableTypeString},
299 	{"oem-logo?", kOFVariableTypeBoolean},
300 	{"output-device", kOFVariableTypeString},
301 	{"output-device-1", kOFVariableTypeString},
302 	{"pci-probe-list", kOFVariableTypeNumber},
303 	{"pci-probe-mask", kOFVariableTypeNumber},
304 	{"real-base", kOFVariableTypeNumber},
305 	{"real-mode?", kOFVariableTypeBoolean},
306 	{"real-size", kOFVariableTypeNumber},
307 	{"screen-#columns", kOFVariableTypeNumber},
308 	{"screen-#rows", kOFVariableTypeNumber},
309 	{"security-mode", kOFVariableTypeString},
310 	{"selftest-#megs", kOFVariableTypeNumber},
311 	{"use-generic?", kOFVariableTypeBoolean},
312 	{"use-nvramrc?", kOFVariableTypeBoolean},
313 	{"virt-base", kOFVariableTypeNumber},
314 	{"virt-size", kOFVariableTypeNumber},
315 
316 #if !defined(__x86_64__)
317 	{"acc-cm-override-charger-count", kOFVariableTypeNumber},
318 	{"acc-cm-override-count", kOFVariableTypeNumber},
319 	{"acc-mb-ld-lifetime", kOFVariableTypeNumber},
320 	{"com.apple.System.boot-nonce", kOFVariableTypeString},
321 	{"darkboot", kOFVariableTypeBoolean},
322 	{"enter-tdm-mode", kOFVariableTypeBoolean},
323 #endif /* !defined(__x86_64__) */
324 	{nullptr, kOFVariableTypeData} // Default type to return
325 };
326 
327 union VariablePermission {
328 	struct {
329 		uint64_t UserWrite            :1;
330 		uint64_t RootRequired         :1;
331 		uint64_t KernelOnly           :1;
332 		uint64_t ResetNVRAMOnlyDelete :1;
333 		uint64_t NeverAllowedToDelete :1;
334 		uint64_t FullAccess           :1;
335 		uint64_t Reserved:58;
336 	} Bits;
337 	uint64_t Uint64;
338 };
339 
340 typedef struct {
341 	const char *name;
342 	VariablePermission p;
343 } VariablePermissionEntry;
344 
345 static const
346 VariablePermissionEntry gVariablePermissions[] = {
347 	{"aapl,pci", .p.Bits.RootRequired = 1},
348 	{"battery-health", .p.Bits.RootRequired = 1,
349 	 .p.Bits.NeverAllowedToDelete = 1},
350 	{"boot-image", .p.Bits.UserWrite = 1},
351 	{"com.apple.System.fp-state", .p.Bits.KernelOnly = 1},
352 	{"policy-nonce-digests", .p.Bits.ResetNVRAMOnlyDelete = 1},
353 	{"security-password", .p.Bits.RootRequired = 1},
354 
355 #if !defined(__x86_64__)
356 	{"acc-cm-override-charger-count", .p.Bits.KernelOnly = 1},
357 	{"acc-cm-override-count", .p.Bits.KernelOnly = 1},
358 	{"acc-mb-ld-lifetime", .p.Bits.KernelOnly = 1},
359 	{"backlight-level", .p.Bits.UserWrite = 1},
360 	{"com.apple.System.boot-nonce", .p.Bits.KernelOnly = 1},
361 	{"com.apple.System.sep.art", .p.Bits.KernelOnly = 1},
362 	{"darkboot", .p.Bits.UserWrite = 1},
363 	{"nonce-seeds", .p.Bits.KernelOnly = 1},
364 #endif /* !defined(__x86_64__) */
365 
366 	{nullptr, {.Bits.FullAccess = 1}} // Default access
367 };
368 
369 static IONVRAMVariableType
370 getVariableType(const char *propName)
371 {
372 	const VariableTypeEntry *entry;
373 
374 	entry = gVariableTypes;
375 	while (entry->name != nullptr) {
376 		if (strcmp(entry->name, propName) == 0) {
377 			break;
378 		}
379 		entry++;
380 	}
381 
382 	return entry->type;
383 }
384 
385 static IONVRAMVariableType
386 getVariableType(const OSSymbol *propSymbol)
387 {
388 	return getVariableType(propSymbol->getCStringNoCopy());
389 }
390 
391 static VariablePermission
392 getVariablePermission(const char *propName)
393 {
394 	const VariablePermissionEntry *entry;
395 
396 	entry = gVariablePermissions;
397 	while (entry->name != nullptr) {
398 		if (strcmp(entry->name, propName) == 0) {
399 			break;
400 		}
401 		entry++;
402 	}
403 
404 	return entry->p;
405 }
406 
407 static bool
408 variableInAllowList(const char *varName)
409 {
410 	unsigned int i = 0;
411 
412 	while (gNVRAMSystemList[i] != nullptr) {
413 		if (strcmp(varName, gNVRAMSystemList[i]) == 0) {
414 			return true;
415 		}
416 		i++;
417 	}
418 
419 	return false;
420 }
421 
422 static bool
423 verifyWriteSizeLimit(const uuid_t *varGuid, const char *variableName, size_t propDataSize)
424 {
425 	if (variableInAllowList(variableName)) {
426 		if (strnstr(variableName, "breadcrumbs", strlen(variableName)) != NULL) {
427 			return propDataSize <= 1024;
428 		} else {
429 			return propDataSize <= 768;
430 		}
431 	}
432 
433 	return true;
434 }
435 
436 static bool
437 verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const char *varName)
438 {
439 	VariablePermission perm;
440 	bool kernel, admin, writeEntitled, readEntitled, allowList, systemGuid, systemEntitled;
441 
442 	perm = getVariablePermission(varName);
443 
444 	kernel = current_task() == kernel_task;
445 
446 	if (perm.Bits.KernelOnly) {
447 		DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
448 		return kernel;
449 	}
450 
451 	allowList = variableInAllowList(varName);
452 	systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0;
453 	admin = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess;
454 	writeEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMWriteAccessKey);
455 	readEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMReadAccessKey);
456 	systemEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMSystemAllowKey) || kernel;
457 
458 	switch (op) {
459 	case kIONVRAMOperationRead:
460 		if (kernel || admin || readEntitled || perm.Bits.FullAccess) {
461 			return true;
462 		}
463 		break;
464 
465 	case kIONVRAMOperationWrite:
466 		if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
467 			if (systemGuid) {
468 				if (allowList) {
469 					if (!systemEntitled) {
470 						DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
471 					}
472 				} else if (!systemEntitled) {
473 					DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
474 					break;
475 				}
476 			}
477 			return true;
478 		}
479 		break;
480 
481 	case kIONVRAMOperationDelete:
482 	case kIONVRAMOperationObliterate:
483 	case kIONVRAMOperationReset:
484 		if (perm.Bits.NeverAllowedToDelete) {
485 			DEBUG_INFO("Never allowed to delete %s\n", varName);
486 			break;
487 		} else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) {
488 			DEBUG_INFO("Not allowed to obliterate %s\n", varName);
489 			break;
490 		}
491 
492 		if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
493 			if (systemGuid) {
494 				if (allowList) {
495 					if (!systemEntitled) {
496 						DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
497 					}
498 				} else if (!systemEntitled) {
499 					DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
500 					break;
501 				}
502 			}
503 			return true;
504 		}
505 		break;
506 	}
507 
508 	DEBUG_INFO("Permission for %s denied, kernel=%d, admin=%d, writeEntitled=%d, readEntitled=%d, systemGuid=%d, systemEntitled=%d\n",
509 	    varName, kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled);
510 	return false;
511 }
512 
513 static bool
514 verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const OSSymbol *varName)
515 {
516 	return verifyPermission(op, varGuid, varName->getCStringNoCopy());
517 }
518 
519 /*
520  * Parse a variable name of the form "GUID:name".
521  * If the name cannot be parsed, substitute the Apple global variable GUID.
522  * Returns TRUE if a GUID was found in the name, FALSE otherwise.
523  * The guidResult and nameResult arguments may be nullptr if you just want
524  * to check the format of the string.
525  */
526 static bool
527 parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult)
528 {
529 	uuid_string_t temp    = {0};
530 	size_t        keyLen  = strlen(key);
531 	bool          result  = false;
532 	const char    *name   = key;
533 	uuid_t        guid;
534 
535 	if (keyLen > sizeof(temp)) {
536 		// check for at least UUID + ":" + more
537 		memcpy(temp, key, sizeof(temp) - 1);
538 
539 		if ((uuid_parse(temp, guid) == 0) &&
540 		    (key[sizeof(temp) - 1] == ':')) {
541 			name = key + sizeof(temp);
542 			result = true;
543 		}
544 	}
545 
546 	if (guidResult) {
547 		result ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
548 	}
549 	if (nameResult) {
550 		*nameResult = name;
551 	}
552 
553 	return false;
554 }
555 
556 // private IOService based class for publishing distinct dictionary properties on
557 // for easy ioreg access since the serializeProperties call is overloaded and is used
558 // as variable access
559 class IODTNVRAMVariables : public IOService
560 {
561 	OSDeclareDefaultStructors(IODTNVRAMVariables)
562 private:
563 	IODTNVRAM        *_provider;
564 	OSDictionary     *_properties;
565 	uuid_t           _guid;
566 
567 public:
568 	bool             init(const uuid_t *guid);
569 	virtual bool     start(IOService * provider) APPLE_KEXT_OVERRIDE;
570 	virtual IOReturn setProperties(OSObject * properties) APPLE_KEXT_OVERRIDE;
571 	virtual bool     serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
572 };
573 
574 OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
575 
576 bool
577 IODTNVRAMVariables::init(const uuid_t *guid)
578 {
579 	require(super::init(), error);
580 	require(guid, error);
581 
582 	uuid_copy(_guid, *guid);
583 
584 	return true;
585 
586 error:
587 	return false;
588 }
589 
590 bool
591 IODTNVRAMVariables::start(IOService * provider)
592 {
593 	require(IOService::start(provider), error);
594 
595 	require(_provider = OSDynamicCast(IODTNVRAM, provider), error);
596 
597 	registerService();
598 
599 	return true;
600 
601 error:
602 	stop(provider);
603 
604 	return false;
605 }
606 
607 IOReturn
608 IODTNVRAMVariables::setProperties(OSObject * properties)
609 {
610 	if (OSDynamicCast(OSDictionary, properties)) {
611 		OSSafeReleaseNULL(_properties);
612 		_properties = OSDynamicCast(OSDictionary, properties);
613 		properties->retain();
614 	}
615 
616 	return IOService::setProperties(properties);
617 }
618 
619 bool
620 IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
621 {
622 	const OSSymbol                    *key;
623 	OSSharedPtr<OSDictionary>         dict;
624 	OSSharedPtr<OSCollectionIterator> iter;
625 	OSSharedPtr<OSDictionary>         localProperties(_properties, OSRetain);
626 	bool                              result = false;
627 
628 	require(localProperties != nullptr, exit);
629 
630 	dict = OSDictionary::withCapacity(localProperties->getCount());
631 	require_action(dict, exit, DEBUG_ERROR("No dictionary\n"));
632 
633 	iter = OSCollectionIterator::withCollection(localProperties.get());
634 	require_action(iter, exit, DEBUG_ERROR("failed to create iterator\n"));
635 
636 	while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
637 		if (verifyPermission(kIONVRAMOperationRead, &_guid, key)) {
638 			dict->setObject(key, localProperties->getObject(key));
639 		}
640 	}
641 
642 	result = dict->serialize(s);
643 
644 exit:
645 	DEBUG_INFO("result=%d\n", result);
646 	return result;
647 }
648 
649 bool
650 IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
651 {
652 	OSSharedPtr<OSDictionary> dict;
653 
654 	if (!super::init(old, plane)) {
655 		return false;
656 	}
657 
658 	_variableLock = IOLockAlloc();
659 	if (!_variableLock) {
660 		return false;
661 	}
662 
663 	PE_parse_boot_argn("nvram-log", &gNVRAMLogging, sizeof(gNVRAMLogging));
664 
665 	dict =  OSDictionary::withCapacity(1);
666 	if (dict == nullptr) {
667 		return false;
668 	}
669 	setPropertyTable(dict.get());
670 	dict.reset();
671 
672 	_nvramSize = getNVRAMSize();
673 	if (_nvramSize == 0) {
674 		DEBUG_ERROR("NVRAM : Error - default size not specified in DT\n");
675 		return false;
676 	}
677 	// partition offsets are UInt16 (bytes / 0x10) + 1
678 	if (_nvramSize > 0xFFFF * 0x10) {
679 		DEBUG_ERROR("NVRAM : truncating _nvramSize from %ld\n", (long) _nvramSize);
680 		_nvramSize = 0xFFFF * 0x10;
681 	}
682 	_nvramImage = IONew(UInt8, _nvramSize);
683 	if (_nvramImage == nullptr) {
684 		return false;
685 	}
686 
687 	_nvramPartitionOffsets = OSDictionary::withCapacity(1);
688 	if (_nvramPartitionOffsets == nullptr) {
689 		return false;
690 	}
691 
692 	_nvramPartitionLengths = OSDictionary::withCapacity(1);
693 	if (_nvramPartitionLengths == nullptr) {
694 		return false;
695 	}
696 
697 	_registryPropertiesKey = OSSymbol::withCStringNoCopy("aapl,pci");
698 	if (_registryPropertiesKey == nullptr) {
699 		return false;
700 	}
701 
702 	// <rdar://problem/9529235> race condition possible between
703 	// IODTNVRAM and IONVRAMController (restore loses boot-args)
704 	initProxyData();
705 
706 	// Require at least the common partition to be present and error free
707 	if (_commonDict == nullptr) {
708 		return false;
709 	}
710 
711 	return true;
712 }
713 
714 void
715 IODTNVRAM::initProxyData(void)
716 {
717 	OSSharedPtr<IORegistryEntry> entry;
718 	const char                   *key = "nvram-proxy-data";
719 	OSData                       *data;
720 	const void                   *bytes;
721 
722 	entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
723 	if (entry != nullptr) {
724 		OSSharedPtr<OSObject> prop = entry->copyProperty(key);
725 		if (prop != nullptr) {
726 			data = OSDynamicCast(OSData, prop.get());
727 			if (data != nullptr) {
728 				bytes = data->getBytesNoCopy();
729 				if ((bytes != nullptr) && (data->getLength() <= _nvramSize)) {
730 					bcopy(bytes, _nvramImage, data->getLength());
731 					initNVRAMImage();
732 					_isProxied = true;
733 				}
734 			}
735 		}
736 		entry->removeProperty(key);
737 	}
738 }
739 
740 UInt32
741 IODTNVRAM::getNVRAMSize(void)
742 {
743 	OSSharedPtr<IORegistryEntry> entry;
744 	const char                   *key = "nvram-total-size";
745 	OSData                       *data;
746 	UInt32                       size = 0;
747 
748 	entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
749 	if (entry != nullptr) {
750 		OSSharedPtr<OSObject> prop = entry->copyProperty(key);
751 		if (prop != nullptr) {
752 			data = OSDynamicCast(OSData, prop.get());
753 			if (data != nullptr) {
754 				size = *((UInt32*)data->getBytesNoCopy());
755 				DEBUG_ALWAYS("NVRAM size is %u bytes\n", (unsigned int) size);
756 			}
757 		}
758 	}
759 	return size;
760 }
761 
762 
763 void
764 IODTNVRAM::registerNVRAMController(IONVRAMController *nvram)
765 {
766 	if (_nvramController != nullptr) {
767 		DEBUG_ERROR("Duplicate controller set\n");
768 		return;
769 	}
770 
771 	DEBUG_INFO("setting controller\n");
772 
773 	_nvramController = nvram;
774 
775 	// <rdar://problem/9529235> race condition possible between
776 	// IODTNVRAM and IONVRAMController (restore loses boot-args)
777 	if (!_isProxied) {
778 		DEBUG_INFO("Proxied NVRAM data\n");
779 		_nvramController->read(0, _nvramImage, _nvramSize);
780 		initNVRAMImage();
781 	}
782 
783 	if (_systemPartitionSize) {
784 		_systemService = new IODTNVRAMVariables;
785 
786 		if (!_systemService || !_systemService->init(&gAppleSystemVariableGuid)) {
787 			DEBUG_ERROR("Unable to start the system service!\n");
788 			goto no_system;
789 		}
790 
791 		_systemService->setName("options-system");
792 
793 		if (!_systemService->attach(this)) {
794 			DEBUG_ERROR("Unable to attach the system service!\n");
795 			OSSafeReleaseNULL(_systemService);
796 			goto no_system;
797 		}
798 
799 		if (!_systemService->start(this)) {
800 			DEBUG_ERROR("Unable to start the system service!\n");
801 			_systemService->detach(this);
802 			OSSafeReleaseNULL(_systemService);
803 			goto no_system;
804 		}
805 	}
806 
807 no_system:
808 	if (_commonPartitionSize) {
809 		_commonService = new IODTNVRAMVariables;
810 
811 		if (!_commonService || !_commonService->init(&gAppleNVRAMGuid)) {
812 			DEBUG_ERROR("Unable to start the common service!\n");
813 			goto no_common;
814 		}
815 
816 		_commonService->setName("options-common");
817 
818 		if (!_commonService->attach(this)) {
819 			DEBUG_ERROR("Unable to attach the common service!\n");
820 			OSSafeReleaseNULL(_commonService);
821 			goto no_common;
822 		}
823 
824 		if (!_commonService->start(this)) {
825 			DEBUG_ERROR("Unable to start the common service!\n");
826 			_systemService->detach(this);
827 			OSSafeReleaseNULL(_commonService);
828 			goto no_common;
829 		}
830 	}
831 
832 no_common:
833 	NVRAMLOCK();
834 	(void) syncVariables();
835 	NVRAMUNLOCK();
836 }
837 
838 void
839 IODTNVRAM::initNVRAMImage(void)
840 {
841 	char   partitionID[18];
842 	UInt32 partitionOffset, partitionLength;
843 	UInt32 currentLength, currentOffset = 0;
844 
845 	_commonPartitionOffset = 0xFFFFFFFF;
846 	_systemPartitionOffset = 0xFFFFFFFF;
847 
848 	// Look through the partitions to find the OF and System partitions.
849 	while (currentOffset < _nvramSize) {
850 		bool common_partition;
851 		bool system_partition;
852 
853 		chrp_nvram_header_t * header = (chrp_nvram_header_t *)(_nvramImage + currentOffset);
854 
855 		currentLength = header->len * NVRAM_CHRP_LENGTH_BLOCK_SIZE;
856 
857 		if (currentLength < sizeof(chrp_nvram_header_t)) {
858 			break;
859 		}
860 
861 		partitionOffset = currentOffset + sizeof(chrp_nvram_header_t);
862 		partitionLength = currentLength - sizeof(chrp_nvram_header_t);
863 
864 		if ((partitionOffset + partitionLength) > _nvramSize) {
865 			break;
866 		}
867 
868 		common_partition = memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_COMMON, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON)) == 0;
869 		system_partition = (memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM)) == 0) ||
870 		    (memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY)) == 0);
871 
872 		if (common_partition) {
873 			_commonPartitionOffset = partitionOffset;
874 			_commonPartitionSize = partitionLength;
875 		} else if (system_partition) {
876 			_systemPartitionOffset = partitionOffset;
877 			_systemPartitionSize = partitionLength;
878 		} else {
879 			OSSharedPtr<OSNumber> partitionOffsetNumber, partitionLengthNumber;
880 
881 			// Construct the partition ID from the signature and name.
882 			snprintf(partitionID, sizeof(partitionID), "0x%02x,", header->sig);
883 			strncpy(partitionID + 5, header->name, sizeof(header->name));
884 			partitionID[17] = '\0';
885 
886 			partitionOffsetNumber = OSNumber::withNumber(partitionOffset, 32);
887 			partitionLengthNumber = OSNumber::withNumber(partitionLength, 32);
888 
889 			// Save the partition offset and length
890 			_nvramPartitionOffsets->setObject(partitionID, partitionOffsetNumber.get());
891 			_nvramPartitionLengths->setObject(partitionID, partitionLengthNumber.get());
892 		}
893 		currentOffset += currentLength;
894 	}
895 
896 	if (_commonPartitionOffset != 0xFFFFFFFF) {
897 		_commonImage = _nvramImage + _commonPartitionOffset;
898 	}
899 
900 	if (_systemPartitionOffset != 0xFFFFFFFF) {
901 		_systemImage = _nvramImage + _systemPartitionOffset;
902 	}
903 
904 	DEBUG_ALWAYS("NVRAM : ofPartitionOffset - 0x%x, ofPartitionSize - 0x%x, systemPartitionOffset - 0x%x, systemPartitionSize - 0x%x\n",
905 	    (unsigned int) _commonPartitionOffset, (unsigned int) _commonPartitionSize, (unsigned int) _systemPartitionOffset, (unsigned int) _systemPartitionSize);
906 
907 	_lastDeviceSync = 0;
908 	_freshInterval = TRUE;          // we will allow sync() even before the first 15 minutes have passed.
909 
910 	initVariables();
911 }
912 
913 void
914 IODTNVRAM::syncInternal(bool rateLimit)
915 {
916 	DEBUG_INFO("rateLimit=%d\n", rateLimit);
917 
918 	// Don't try to perform controller operations if none has been registered.
919 	if (_nvramController == nullptr) {
920 		return;
921 	}
922 
923 	// Rate limit requests to sync. Drivers that need this rate limiting will
924 	// shadow the data and only write to flash when they get a sync call
925 	if (rateLimit && !safeToSync()) {
926 		return;
927 	}
928 
929 	DEBUG_INFO("Calling sync()\n");
930 	NVRAMLOCK();
931 	_nvramController->sync();
932 	NVRAMUNLOCK();
933 }
934 
935 void
936 IODTNVRAM::sync(void)
937 {
938 	syncInternal(false);
939 }
940 
941 bool
942 IODTNVRAM::serializeProperties(OSSerialize *s) const
943 {
944 	const OSSymbol                    *key;
945 	OSSharedPtr<OSDictionary>         dict;
946 	OSSharedPtr<OSCollectionIterator> iter;
947 	bool                              result = false;
948 	unsigned int                      totalCapacity = 0;
949 
950 	NVRAMLOCK();
951 	if (_commonDict) {
952 		totalCapacity += _commonDict->getCapacity();
953 	}
954 
955 	if (_systemDict) {
956 		totalCapacity += _systemDict->getCapacity();
957 	}
958 
959 	dict = OSDictionary::withCapacity(totalCapacity);
960 
961 	if (dict == nullptr) {
962 		DEBUG_ERROR("No dictionary\n");
963 		goto unlock;
964 	}
965 
966 	// Copy system entries first if present then copy unique common entries
967 	if (_systemDict != nullptr) {
968 		iter = OSCollectionIterator::withCollection(_systemDict.get());
969 		if (iter == nullptr) {
970 			DEBUG_ERROR("failed to create iterator\n");
971 			goto unlock;
972 		}
973 
974 		while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
975 			if (verifyPermission(kIONVRAMOperationRead, &gAppleSystemVariableGuid, key)) {
976 				dict->setObject(key, _systemDict->getObject(key));
977 			}
978 		}
979 
980 		iter.reset();
981 	}
982 
983 	if (_commonDict != nullptr) {
984 		iter = OSCollectionIterator::withCollection(_commonDict.get());
985 		if (iter == nullptr) {
986 			DEBUG_ERROR("failed to create common iterator\n");
987 			goto unlock;
988 		}
989 
990 		while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
991 			if (dict->getObject(key) != nullptr) {
992 				// Skip non uniques
993 				continue;
994 			}
995 			if (verifyPermission(kIONVRAMOperationRead, &gAppleNVRAMGuid, key)) {
996 				dict->setObject(key, _commonDict->getObject(key));
997 			}
998 		}
999 	}
1000 
1001 	result = dict->serialize(s);
1002 
1003 unlock:
1004 	NVRAMUNLOCK();
1005 
1006 	DEBUG_INFO("result=%d\n", result);
1007 
1008 	return result;
1009 }
1010 
1011 IOReturn
1012 IODTNVRAM::chooseDictionary(IONVRAMOperation operation, const uuid_t *varGuid, const char *variableName, OSDictionary **dict) const
1013 {
1014 	if (_systemDict != nullptr) {
1015 		bool systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0;
1016 
1017 		if (variableInAllowList(variableName)) {
1018 			DEBUG_INFO("Using system dictionary due to allow list\n");
1019 			if (!systemGuid) {
1020 				DEBUG_ERROR("System GUID NOT used for %s\n", variableName);
1021 			}
1022 			*dict = _systemDict.get();
1023 		} else if (systemGuid) {
1024 			DEBUG_INFO("Using system dictionary via GUID\n");
1025 			*dict = _systemDict.get();
1026 		} else {
1027 			DEBUG_INFO("Using common dictionary\n");
1028 			*dict = _commonDict.get();
1029 		}
1030 	} else {
1031 		DEBUG_INFO("Defaulting to common dictionary\n");
1032 		*dict = _commonDict.get();
1033 	}
1034 
1035 	return kIOReturnSuccess;
1036 }
1037 
1038 bool
1039 IODTNVRAM::handleSpecialVariables(const char *name, uuid_t *guid, OSObject *obj, IOReturn *error)
1040 {
1041 	IOReturn err = kIOReturnSuccess;
1042 	bool special = false;
1043 
1044 	NVRAMLOCKASSERT();
1045 
1046 	if (strcmp(name, "ResetNVRam") == 0) {
1047 		DEBUG_INFO("%s requested\n", name);
1048 
1049 		if (uuid_compare(*guid, gAppleSystemVariableGuid) == 0) {
1050 			if (_systemDict != nullptr) {
1051 				_systemDict->flushCollection();
1052 			}
1053 
1054 			_commonDict->flushCollection();
1055 			DEBUG_INFO("system & common dictionary flushed\n");
1056 
1057 			err = syncVariables();
1058 		}
1059 
1060 		special = true;
1061 	} else if (strcmp(name, "ObliterateNVRam") == 0) {
1062 		DEBUG_INFO("%s requested\n", name);
1063 
1064 		if ((_systemDict != nullptr) && (uuid_compare(*guid, gAppleSystemVariableGuid) == 0)) {
1065 			const OSSymbol *key;
1066 			OSSharedPtr<OSDictionary> newDict;
1067 			OSSharedPtr<OSCollectionIterator> iter;
1068 
1069 			newDict = OSDictionary::withCapacity(_systemDict->getCapacity());
1070 			iter = OSCollectionIterator::withCollection(newDict.get());
1071 			if ((newDict == nullptr) || (iter == nullptr)) {
1072 				err = kIOReturnNoMemory;
1073 				goto exit;
1074 			}
1075 
1076 			while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1077 				const OSSymbol *key = OSDynamicCast(OSSymbol, iter->getNextObject());
1078 				if (key == nullptr) {
1079 					err = kIOReturnNoMemory;
1080 					goto exit;
1081 				}
1082 
1083 				if (!verifyPermission(kIONVRAMOperationObliterate, &gAppleSystemVariableGuid, key)) {
1084 					newDict->setObject(key, _systemDict->getObject(key));
1085 				}
1086 			}
1087 
1088 			_systemDict = newDict;
1089 
1090 			DEBUG_INFO("system dictionary flushed\n");
1091 		} else if (_commonDict != nullptr) {
1092 			const OSSymbol *key;
1093 			OSSharedPtr<OSDictionary> newDict;
1094 			OSSharedPtr<OSCollectionIterator> iter;
1095 
1096 			newDict = OSDictionary::withCapacity(_commonDict->getCapacity());
1097 			iter = OSCollectionIterator::withCollection(newDict.get());
1098 			if ((newDict == nullptr) || (iter == nullptr)) {
1099 				err = kIOReturnNoMemory;
1100 				goto exit;
1101 			}
1102 
1103 			while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1104 				if (!verifyPermission(kIONVRAMOperationObliterate, &gAppleNVRAMGuid, key)) {
1105 					newDict->setObject(key, _commonDict->getObject(key));
1106 				}
1107 			}
1108 
1109 			_commonDict = newDict;
1110 
1111 			DEBUG_INFO("common dictionary flushed\n");
1112 		}
1113 
1114 		special = true;
1115 		err = syncVariables();
1116 	}
1117 
1118 exit:
1119 	if (error) {
1120 		*error = err;
1121 	}
1122 
1123 	return special;
1124 }
1125 
1126 OSSharedPtr<OSObject>
1127 IODTNVRAM::copyProperty(const OSSymbol *aKey) const
1128 {
1129 	IOReturn              result;
1130 	const char            *variableName;
1131 	uuid_t                varGuid;
1132 	OSDictionary          *dict;
1133 	OSSharedPtr<OSObject> theObject = nullptr;
1134 
1135 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1136 
1137 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1138 
1139 	result = chooseDictionary(kIONVRAMOperationRead, &varGuid, variableName, &dict);
1140 	if (result != kIOReturnSuccess) {
1141 		goto exit;
1142 	}
1143 
1144 	if (!verifyPermission(kIONVRAMOperationRead, &varGuid, variableName)) {
1145 		DEBUG_INFO("Not privileged\n");
1146 		goto exit;
1147 	}
1148 
1149 	NVRAMLOCK();
1150 	theObject.reset(dict->getObject(variableName), OSRetain);
1151 	NVRAMUNLOCK();
1152 
1153 	if (theObject != nullptr) {
1154 		DEBUG_INFO("found data\n");
1155 	}
1156 
1157 exit:
1158 	return theObject;
1159 }
1160 
1161 OSSharedPtr<OSObject>
1162 IODTNVRAM::copyProperty(const char *aKey) const
1163 {
1164 	OSSharedPtr<const OSSymbol> keySymbol;
1165 	OSSharedPtr<OSObject>       theObject;
1166 
1167 	keySymbol = OSSymbol::withCString(aKey);
1168 	if (keySymbol != nullptr) {
1169 		theObject = copyProperty(keySymbol.get());
1170 	}
1171 
1172 	return theObject;
1173 }
1174 
1175 OSObject *
1176 IODTNVRAM::getProperty(const OSSymbol *aKey) const
1177 {
1178 	// The shared pointer gets released at the end of the function,
1179 	// and returns a view into theObject.
1180 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1181 
1182 	return theObject.get();
1183 }
1184 
1185 OSObject *
1186 IODTNVRAM::getProperty(const char *aKey) const
1187 {
1188 	// The shared pointer gets released at the end of the function,
1189 	// and returns a view into theObject.
1190 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1191 
1192 	return theObject.get();
1193 }
1194 
1195 IOReturn
1196 IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
1197 {
1198 	IOReturn              result = kIOReturnSuccess;
1199 	bool                  remove = false;
1200 	OSString              *tmpString = nullptr;
1201 	OSSharedPtr<OSObject> propObject, oldObject;
1202 	OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
1203 	const char            *variableName;
1204 	uuid_t                varGuid;
1205 	OSDictionary          *dict;
1206 	bool                  deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
1207 	size_t                propDataSize = 0;
1208 
1209 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1210 
1211 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1212 	deletePropertyKey = strncmp(variableName, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
1213 	syncNowPropertyKey = strncmp(variableName, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
1214 	forceSyncNowPropertyKey = strncmp(variableName, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
1215 
1216 	if (deletePropertyKey) {
1217 		tmpString = OSDynamicCast(OSString, anObject);
1218 		if (tmpString != nullptr) {
1219 			DEBUG_INFO("kIONVRAMDeletePropertyKey found\n");
1220 			OSSharedPtr<const OSSymbol> sharedKey = OSSymbol::withString(tmpString);
1221 			removeProperty(sharedKey.get());
1222 		} else {
1223 			DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1224 			result = kIOReturnError;
1225 		}
1226 		goto exit;
1227 	} else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
1228 		tmpString = OSDynamicCast(OSString, anObject);
1229 		DEBUG_INFO("NVRAM sync key %s found\n", aKey->getCStringNoCopy());
1230 		if (tmpString != nullptr) {
1231 			// We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1232 			syncInternal(syncNowPropertyKey);
1233 		} else {
1234 			DEBUG_INFO("%s value needs to be an OSString\n", variableName);
1235 			result = kIOReturnError;
1236 		}
1237 		goto exit;
1238 	}
1239 
1240 	result = chooseDictionary(kIONVRAMOperationWrite, &varGuid, variableName, &dict);
1241 	if (result != kIOReturnSuccess) {
1242 		goto exit;
1243 	}
1244 
1245 	if (!verifyPermission(kIONVRAMOperationWrite, &varGuid, variableName)) {
1246 		DEBUG_INFO("Not privileged\n");
1247 		result = kIOReturnNotPrivileged;
1248 		goto exit;
1249 	}
1250 
1251 	// Make sure the object is of the correct type.
1252 	switch (getVariableType(variableName)) {
1253 	case kOFVariableTypeBoolean:
1254 		propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
1255 		break;
1256 
1257 	case kOFVariableTypeNumber:
1258 		propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
1259 		break;
1260 
1261 	case kOFVariableTypeString:
1262 		propObject = OSDynamicPtrCast<OSString>(sharedObject);
1263 		if (propObject != nullptr) {
1264 			propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
1265 
1266 			if (aKey->isEqualTo(kIONVRAMBootArgsKey) && (propDataSize >= BOOT_LINE_LENGTH)) {
1267 				DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
1268 				result = kIOReturnNoSpace;
1269 				goto exit;
1270 			}
1271 		}
1272 		break;
1273 
1274 	case kOFVariableTypeData:
1275 		propObject = OSDynamicPtrCast<OSData>(sharedObject);
1276 		if (propObject == nullptr) {
1277 			tmpString = OSDynamicCast(OSString, sharedObject.get());
1278 			if (tmpString != nullptr) {
1279 				propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
1280 				    tmpString->getLength());
1281 			}
1282 		}
1283 
1284 		if (propObject != nullptr) {
1285 			propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
1286 		}
1287 
1288 #if defined(XNU_TARGET_OS_OSX)
1289 		if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
1290 			remove = true;
1291 		}
1292 #endif /* defined(XNU_TARGET_OS_OSX) */
1293 		break;
1294 	default:
1295 		break;
1296 	}
1297 
1298 	if (propObject == nullptr) {
1299 		DEBUG_INFO("No property object\n");
1300 		result = kIOReturnBadArgument;
1301 		goto exit;
1302 	}
1303 
1304 	if (!verifyWriteSizeLimit(&varGuid, variableName, propDataSize)) {
1305 		DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, variableName);
1306 		result = kIOReturnNoSpace;
1307 		goto exit;
1308 	}
1309 
1310 	NVRAMLOCK();
1311 
1312 	if (handleSpecialVariables(variableName, &varGuid, propObject.get(), &result)) {
1313 		goto unlock;
1314 	}
1315 
1316 	oldObject.reset(dict->getObject(variableName), OSRetain);
1317 	if (remove == false) {
1318 		DEBUG_INFO("Adding object\n");
1319 		if (!dict->setObject(variableName, propObject.get())) {
1320 			result = kIOReturnBadArgument;
1321 		}
1322 	} else {
1323 		DEBUG_INFO("Removing object\n");
1324 		// Check for existence so we can decide whether we need to sync variables
1325 		if (oldObject) {
1326 			result = removePropertyInternal(aKey);
1327 		} else {
1328 			result = kIOReturnNotFound;
1329 		}
1330 	}
1331 
1332 	if (result == kIOReturnSuccess) {
1333 		result = syncVariables();
1334 		if (result != kIOReturnSuccess) {
1335 			DEBUG_ERROR("syncVariables failed, result=0x%08x\n", result);
1336 			if (oldObject) {
1337 				dict->setObject(variableName, oldObject.get());
1338 			} else {
1339 				dict->removeObject(variableName);
1340 			}
1341 			(void) syncVariables();
1342 			result = kIOReturnNoMemory;
1343 		}
1344 	}
1345 
1346 	if (oldObject) {
1347 		oldObject.reset();
1348 	}
1349 	if (tmpString) {
1350 		propObject.reset();
1351 	}
1352 
1353 unlock:
1354 	NVRAMUNLOCK();
1355 
1356 exit:
1357 	DEBUG_INFO("result=0x%08x\n", result);
1358 
1359 	return result;
1360 }
1361 
1362 bool
1363 IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
1364 {
1365 	return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
1366 }
1367 
1368 void
1369 IODTNVRAM::removeProperty(const OSSymbol *aKey)
1370 {
1371 	IOReturn ret;
1372 
1373 	NVRAMLOCK();
1374 
1375 	ret = removePropertyInternal(aKey);
1376 
1377 	NVRAMUNLOCK();
1378 
1379 	if (ret != kIOReturnSuccess) {
1380 		DEBUG_INFO("removePropertyInternal failed, ret=0x%08x\n", ret);
1381 	}
1382 }
1383 
1384 IOReturn
1385 IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
1386 {
1387 	IOReturn     result;
1388 	const char   *variableName;
1389 	uuid_t       varGuid;
1390 	OSDictionary *dict;
1391 
1392 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1393 
1394 	NVRAMLOCKASSERT();
1395 
1396 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1397 
1398 	result = chooseDictionary(kIONVRAMOperationDelete, &varGuid, variableName, &dict);
1399 	if (result != kIOReturnSuccess) {
1400 		goto exit;
1401 	}
1402 
1403 	if (!verifyPermission(kIONVRAMOperationDelete, &varGuid, variableName)) {
1404 		DEBUG_INFO("Not priveleged\n");
1405 		result = kIOReturnNotPrivileged;
1406 		goto exit;
1407 	}
1408 
1409 	// If the object exists, remove it from the dictionary.
1410 	if (dict->getObject(variableName) != nullptr) {
1411 		dict->removeObject(variableName);
1412 		result = syncVariables();
1413 	}
1414 
1415 exit:
1416 	return result;
1417 }
1418 
1419 IOReturn
1420 IODTNVRAM::setProperties(OSObject *properties)
1421 {
1422 	IOReturn                          result = kIOReturnSuccess;
1423 	OSObject                          *object;
1424 	const OSSymbol                    *key;
1425 	OSDictionary                      *dict;
1426 	OSSharedPtr<OSCollectionIterator> iter;
1427 
1428 	dict = OSDynamicCast(OSDictionary, properties);
1429 	if (dict == nullptr) {
1430 		DEBUG_ERROR("Not a dictionary\n");
1431 		return kIOReturnBadArgument;
1432 	}
1433 
1434 	iter = OSCollectionIterator::withCollection(dict);
1435 	if (iter == nullptr) {
1436 		DEBUG_ERROR("Couldn't create iterator\n");
1437 		return kIOReturnBadArgument;
1438 	}
1439 
1440 	while (result == kIOReturnSuccess) {
1441 		key = OSDynamicCast(OSSymbol, iter->getNextObject());
1442 		if (key == nullptr) {
1443 			break;
1444 		}
1445 
1446 		object = dict->getObject(key);
1447 		if (object == nullptr) {
1448 			continue;
1449 		}
1450 
1451 		result = setPropertyInternal(key, object);
1452 	}
1453 
1454 	DEBUG_INFO("result=0x%08x\n", result);
1455 
1456 	return result;
1457 }
1458 
1459 IOReturn
1460 IODTNVRAM::readXPRAM(IOByteCount offset, UInt8 *buffer,
1461     IOByteCount length)
1462 {
1463 	return kIOReturnUnsupported;
1464 }
1465 
1466 IOReturn
1467 IODTNVRAM::writeXPRAM(IOByteCount offset, UInt8 *buffer,
1468     IOByteCount length)
1469 {
1470 	return kIOReturnUnsupported;
1471 }
1472 
1473 IOReturn
1474 IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
1475     const OSSymbol **name,
1476     OSData **value)
1477 {
1478 	IOReturn err;
1479 
1480 	err = readNVRAMPropertyType1(entry, name, value);
1481 
1482 	return err;
1483 }
1484 
1485 IOReturn
1486 IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
1487     const OSSymbol *name,
1488     OSData *value)
1489 {
1490 	IOReturn err;
1491 
1492 	err = writeNVRAMPropertyType1(entry, name, value);
1493 
1494 	return err;
1495 }
1496 
1497 OSDictionary *
1498 IODTNVRAM::getNVRAMPartitions(void)
1499 {
1500 	return _nvramPartitionLengths.get();
1501 }
1502 
1503 IOReturn
1504 IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
1505     IOByteCount offset, UInt8 *buffer,
1506     IOByteCount length)
1507 {
1508 	OSNumber *partitionOffsetNumber, *partitionLengthNumber;
1509 	UInt32   partitionOffset, partitionLength, end;
1510 
1511 	partitionOffsetNumber =
1512 	    (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
1513 	partitionLengthNumber =
1514 	    (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
1515 
1516 	if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
1517 		return kIOReturnNotFound;
1518 	}
1519 
1520 	partitionOffset = partitionOffsetNumber->unsigned32BitValue();
1521 	partitionLength = partitionLengthNumber->unsigned32BitValue();
1522 
1523 	if (os_add_overflow(offset, length, &end)) {
1524 		return kIOReturnBadArgument;
1525 	}
1526 	if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
1527 		return kIOReturnBadArgument;
1528 	}
1529 
1530 	bcopy(_nvramImage + partitionOffset + offset, buffer, length);
1531 
1532 	return kIOReturnSuccess;
1533 }
1534 
1535 IOReturn
1536 IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
1537     IOByteCount offset, UInt8 *buffer,
1538     IOByteCount length)
1539 {
1540 	OSNumber *partitionOffsetNumber, *partitionLengthNumber;
1541 	UInt32   partitionOffset, partitionLength, end;
1542 
1543 	partitionOffsetNumber =
1544 	    (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
1545 	partitionLengthNumber =
1546 	    (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
1547 
1548 	if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
1549 		return kIOReturnNotFound;
1550 	}
1551 
1552 	partitionOffset = partitionOffsetNumber->unsigned32BitValue();
1553 	partitionLength = partitionLengthNumber->unsigned32BitValue();
1554 
1555 	if (os_add_overflow(offset, length, &end)) {
1556 		return kIOReturnBadArgument;
1557 	}
1558 	if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
1559 		return kIOReturnBadArgument;
1560 	}
1561 
1562 	bcopy(buffer, _nvramImage + partitionOffset + offset, length);
1563 
1564 	if (_nvramController != nullptr) {
1565 		_nvramController->write(0, _nvramImage, _nvramSize);
1566 	}
1567 
1568 	return kIOReturnSuccess;
1569 }
1570 
1571 IOByteCount
1572 IODTNVRAM::savePanicInfo(UInt8 *buffer, IOByteCount length)
1573 {
1574 	return 0;
1575 }
1576 
1577 // Private methods
1578 
1579 UInt8
1580 IODTNVRAM::calculatePartitionChecksum(UInt8 *partitionHeader)
1581 {
1582 	UInt8 cnt, isum, csum = 0;
1583 
1584 	for (cnt = 0; cnt < 0x10; cnt++) {
1585 		isum = csum + partitionHeader[cnt];
1586 		if (isum < csum) {
1587 			isum++;
1588 		}
1589 		csum = isum;
1590 	}
1591 
1592 	return csum;
1593 }
1594 
1595 IOReturn
1596 IODTNVRAM::initVariables(void)
1597 {
1598 	UInt32                      cnt;
1599 	UInt8                       *propName, *propData;
1600 	UInt32                      propNameLength, propDataLength, regionIndex;
1601 	OSSharedPtr<const OSSymbol> propSymbol;
1602 	OSSharedPtr<OSObject>       propObject;
1603 	NVRAMRegionInfo             *currentRegion;
1604 
1605 	NVRAMRegionInfo             variableRegions[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
1606 							  { NVRAM_CHRP_PARTITION_NAME_SYSTEM, _systemPartitionOffset, _systemPartitionSize, _systemDict, _systemImage} };
1607 
1608 	DEBUG_INFO("...\n");
1609 
1610 	for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
1611 		currentRegion = &variableRegions[regionIndex];
1612 
1613 		if (currentRegion->size == 0) {
1614 			continue;
1615 		}
1616 
1617 		currentRegion->dict = OSDictionary::withCapacity(1);
1618 
1619 		DEBUG_INFO("region = %s\n", currentRegion->name);
1620 		cnt = 0;
1621 		while (cnt < currentRegion->size) {
1622 			// Break if there is no name.
1623 			if (currentRegion->image[cnt] == '\0') {
1624 				break;
1625 			}
1626 
1627 			// Find the length of the name.
1628 			propName = currentRegion->image + cnt;
1629 			for (propNameLength = 0; (cnt + propNameLength) < currentRegion->size;
1630 			    propNameLength++) {
1631 				if (currentRegion->image[cnt + propNameLength] == '=') {
1632 					break;
1633 				}
1634 			}
1635 
1636 			// Break if the name goes past the end of the partition.
1637 			if ((cnt + propNameLength) >= currentRegion->size) {
1638 				break;
1639 			}
1640 			cnt += propNameLength + 1;
1641 
1642 			propData = currentRegion->image + cnt;
1643 			for (propDataLength = 0; (cnt + propDataLength) < currentRegion->size;
1644 			    propDataLength++) {
1645 				if (currentRegion->image[cnt + propDataLength] == '\0') {
1646 					break;
1647 				}
1648 			}
1649 
1650 			// Break if the data goes past the end of the partition.
1651 			if ((cnt + propDataLength) >= currentRegion->size) {
1652 				break;
1653 			}
1654 			cnt += propDataLength + 1;
1655 
1656 			if (convertPropToObject(propName, propNameLength,
1657 			    propData, propDataLength,
1658 			    propSymbol, propObject)) {
1659 				DEBUG_INFO("adding %s, dataLength=%u\n", propSymbol.get()->getCStringNoCopy(), (unsigned int)propDataLength);
1660 				currentRegion->dict.get()->setObject(propSymbol.get(), propObject.get());
1661 			}
1662 		}
1663 	}
1664 
1665 	// Create the boot-args property if it is not in the dictionary.
1666 	if (_commonDict->getObject(kIONVRAMBootArgsKey) == nullptr) {
1667 		propObject = OSString::withCStringNoCopy("");
1668 		if (propObject != nullptr) {
1669 			_commonDict->setObject(kIONVRAMBootArgsKey, propObject.get());
1670 		}
1671 	}
1672 
1673 	DEBUG_INFO("%s _commonDict=%p _systemDict=%p\n", __FUNCTION__, _commonDict.get(), _systemDict.get());
1674 
1675 	return kIOReturnSuccess;
1676 }
1677 
1678 IOReturn
1679 IODTNVRAM::syncOFVariables(void)
1680 {
1681 	return kIOReturnUnsupported;
1682 }
1683 
1684 IOReturn
1685 IODTNVRAM::syncVariables(void)
1686 {
1687 	bool                              ok;
1688 	UInt32                            length, maxLength, regionIndex;
1689 	UInt8                             *buffer, *tmpBuffer;
1690 	const OSSymbol                    *tmpSymbol;
1691 	OSObject                          *tmpObject;
1692 	OSSharedPtr<OSCollectionIterator> iter;
1693 	NVRAMRegionInfo                   *currentRegion;
1694 
1695 	NVRAMRegionInfo             variableRegions[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
1696 							  { NVRAM_CHRP_PARTITION_NAME_SYSTEM, _systemPartitionOffset, _systemPartitionSize, _systemDict, _systemImage} };
1697 
1698 	NVRAMLOCKASSERT();
1699 
1700 	if (_systemPanicked) {
1701 		return kIOReturnNotReady;
1702 	}
1703 
1704 	if (_nvramController == nullptr) {
1705 		DEBUG_ERROR("No _nvramController\n");
1706 		return kIOReturnNotReady;
1707 	}
1708 
1709 	DEBUG_INFO("...\n");
1710 
1711 	for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
1712 		OSSharedPtr<OSNumber> sizeUsed;
1713 		currentRegion = &variableRegions[regionIndex];
1714 
1715 		if (currentRegion->size == 0) {
1716 			continue;
1717 		}
1718 
1719 		DEBUG_INFO("region = %s\n", currentRegion->name);
1720 		buffer = tmpBuffer = IONew(UInt8, currentRegion->size);
1721 		if (buffer == nullptr) {
1722 			return kIOReturnNoMemory;
1723 		}
1724 		bzero(buffer, currentRegion->size);
1725 
1726 		ok = true;
1727 		maxLength = currentRegion->size;
1728 
1729 		iter = OSCollectionIterator::withCollection(currentRegion->dict.get());
1730 		if (iter == nullptr) {
1731 			ok = false;
1732 		}
1733 
1734 		while (ok) {
1735 			tmpSymbol = OSDynamicCast(OSSymbol, iter->getNextObject());
1736 			if (tmpSymbol == nullptr) {
1737 				break;
1738 			}
1739 
1740 			DEBUG_INFO("adding variable %s\n", tmpSymbol->getCStringNoCopy());
1741 
1742 			tmpObject = currentRegion->dict->getObject(tmpSymbol);
1743 
1744 			length = maxLength;
1745 			ok = convertObjectToProp(tmpBuffer, &length, tmpSymbol, tmpObject);
1746 			if (ok) {
1747 				tmpBuffer += length;
1748 				maxLength -= length;
1749 			}
1750 		}
1751 
1752 		if (ok) {
1753 			bcopy(buffer, currentRegion->image, currentRegion->size);
1754 		}
1755 
1756 		IODelete(buffer, UInt8, currentRegion->size);
1757 
1758 		sizeUsed = OSNumber::withNumber(maxLength, 32);
1759 		_nvramController->setProperty(currentRegion->name, sizeUsed.get());
1760 		sizeUsed.reset();
1761 
1762 		if ((strncmp(currentRegion->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM)) == 0) &&
1763 		    (_systemService != nullptr)) {
1764 			_systemService->setProperties(_systemDict.get());
1765 		} else if ((strncmp(currentRegion->name, NVRAM_CHRP_PARTITION_NAME_COMMON, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON)) == 0) &&
1766 		    (_commonService != nullptr)) {
1767 			_commonService->setProperties(_commonDict.get());
1768 		}
1769 
1770 		if (!ok) {
1771 			return kIOReturnBadArgument;
1772 		}
1773 	}
1774 
1775 	DEBUG_INFO("ok=%d\n", ok);
1776 
1777 	return _nvramController->write(0, _nvramImage, _nvramSize);
1778 }
1779 
1780 UInt32
1781 IODTNVRAM::getOFVariableType(const char *propName) const
1782 {
1783 	return 0;
1784 }
1785 
1786 UInt32
1787 IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const
1788 {
1789 	return 0;
1790 }
1791 
1792 
1793 UInt32
1794 IODTNVRAM::getOFVariablePerm(const char *propName) const
1795 {
1796 	return 0;
1797 }
1798 
1799 UInt32
1800 IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
1801 {
1802 	return 0;
1803 }
1804 
1805 bool
1806 IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol,
1807     UInt32 *propType, UInt32 *propOffset)
1808 {
1809 	/* UNSUPPORTED */
1810 	return false;
1811 }
1812 bool
1813 IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
1814     UInt8 *propData, UInt32 propDataLength,
1815     const OSSymbol **propSymbol,
1816     OSObject **propObject)
1817 {
1818 	OSSharedPtr<const OSSymbol> tmpSymbol;
1819 	OSSharedPtr<OSNumber>       tmpNumber;
1820 	OSSharedPtr<OSString>       tmpString;
1821 	OSSharedPtr<OSObject>       tmpObject = nullptr;
1822 
1823 	propName[propNameLength] = '\0';
1824 	tmpSymbol = OSSymbol::withCString((const char *)propName);
1825 	propName[propNameLength] = '=';
1826 	if (tmpSymbol == nullptr) {
1827 		return false;
1828 	}
1829 
1830 	switch (getVariableType(tmpSymbol.get())) {
1831 	case kOFVariableTypeBoolean:
1832 		if (!strncmp("true", (const char *)propData, propDataLength)) {
1833 			tmpObject.reset(kOSBooleanTrue, OSRetain);
1834 		} else if (!strncmp("false", (const char *)propData, propDataLength)) {
1835 			tmpObject.reset(kOSBooleanFalse, OSRetain);
1836 		}
1837 		break;
1838 
1839 	case kOFVariableTypeNumber:
1840 		tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32);
1841 		if (tmpNumber != nullptr) {
1842 			tmpObject = tmpNumber;
1843 		}
1844 		break;
1845 
1846 	case kOFVariableTypeString:
1847 		tmpString = OSString::withCString((const char *)propData);
1848 		if (tmpString != nullptr) {
1849 			tmpObject = tmpString;
1850 		}
1851 		break;
1852 
1853 	case kOFVariableTypeData:
1854 		tmpObject = unescapeBytesToData(propData, propDataLength);
1855 		break;
1856 
1857 	default:
1858 		break;
1859 	}
1860 
1861 	if (tmpObject == nullptr) {
1862 		tmpSymbol.reset();
1863 		return false;
1864 	}
1865 
1866 	*propSymbol = tmpSymbol.detach();
1867 	*propObject = tmpObject.detach();
1868 
1869 	return true;
1870 }
1871 
1872 bool
1873 IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
1874     UInt8 *propData, UInt32 propDataLength,
1875     OSSharedPtr<const OSSymbol>& propSymbol,
1876     OSSharedPtr<OSObject>& propObject)
1877 {
1878 	const OSSymbol* propSymbolRaw = nullptr;
1879 	OSObject* propObjectRaw = nullptr;
1880 	bool result = convertPropToObject(propName, propNameLength, propData, propDataLength,
1881 	    &propSymbolRaw, &propObjectRaw);
1882 	propSymbol.reset(propSymbolRaw, OSNoRetain);
1883 	propObject.reset(propObjectRaw, OSNoRetain);
1884 	return result;
1885 }
1886 
1887 bool
1888 IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length,
1889     const OSSymbol *propSymbol, OSObject *propObject)
1890 {
1891 	const UInt8          *propName;
1892 	UInt32               propNameLength, propDataLength, remaining;
1893 	IONVRAMVariableType  propType;
1894 	OSBoolean            *tmpBoolean = nullptr;
1895 	OSNumber             *tmpNumber = nullptr;
1896 	OSString             *tmpString = nullptr;
1897 	OSSharedPtr<OSData>  tmpData;
1898 
1899 	propName = (const UInt8 *)propSymbol->getCStringNoCopy();
1900 	propNameLength = propSymbol->getLength();
1901 	propType = getVariableType(propSymbol);
1902 
1903 	// Get the size of the data.
1904 	propDataLength = 0xFFFFFFFF;
1905 	switch (propType) {
1906 	case kOFVariableTypeBoolean:
1907 		tmpBoolean = OSDynamicCast(OSBoolean, propObject);
1908 		if (tmpBoolean != nullptr) {
1909 			propDataLength = 5;
1910 		}
1911 		break;
1912 
1913 	case kOFVariableTypeNumber:
1914 		tmpNumber = OSDynamicCast(OSNumber, propObject);
1915 		if (tmpNumber != nullptr) {
1916 			propDataLength = 10;
1917 		}
1918 		break;
1919 
1920 	case kOFVariableTypeString:
1921 		tmpString = OSDynamicCast(OSString, propObject);
1922 		if (tmpString != nullptr) {
1923 			propDataLength = tmpString->getLength();
1924 		}
1925 		break;
1926 
1927 	case kOFVariableTypeData:
1928 		tmpData.reset(OSDynamicCast(OSData, propObject), OSNoRetain);
1929 		if (tmpData != nullptr) {
1930 			tmpData = escapeDataToData(tmpData.detach());
1931 			propDataLength = tmpData->getLength();
1932 		}
1933 		break;
1934 
1935 	default:
1936 		break;
1937 	}
1938 
1939 	// Make sure the propertySize is known and will fit.
1940 	if (propDataLength == 0xFFFFFFFF) {
1941 		return false;
1942 	}
1943 	if ((propNameLength + propDataLength + 2) > *length) {
1944 		return false;
1945 	}
1946 
1947 	// Copy the property name equal sign.
1948 	buffer += snprintf((char *)buffer, *length, "%s=", propName);
1949 	remaining = *length - propNameLength - 1;
1950 
1951 	switch (propType) {
1952 	case kOFVariableTypeBoolean:
1953 		if (tmpBoolean->getValue()) {
1954 			strlcpy((char *)buffer, "true", remaining);
1955 		} else {
1956 			strlcpy((char *)buffer, "false", remaining);
1957 		}
1958 		break;
1959 
1960 	case kOFVariableTypeNumber:
1961 	{
1962 		uint32_t tmpValue = tmpNumber->unsigned32BitValue();
1963 		if (tmpValue == 0xFFFFFFFF) {
1964 			strlcpy((char *)buffer, "-1", remaining);
1965 		} else if (tmpValue < 1000) {
1966 			snprintf((char *)buffer, remaining, "%d", (uint32_t)tmpValue);
1967 		} else {
1968 			snprintf((char *)buffer, remaining, "0x%x", (uint32_t)tmpValue);
1969 		}
1970 	}
1971 	break;
1972 
1973 	case kOFVariableTypeString:
1974 		strlcpy((char *)buffer, tmpString->getCStringNoCopy(), remaining);
1975 		break;
1976 
1977 	case kOFVariableTypeData:
1978 		bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength);
1979 		tmpData.reset();
1980 		break;
1981 
1982 	default:
1983 		break;
1984 	}
1985 
1986 	propDataLength = ((UInt32) strlen((const char *)buffer));
1987 
1988 	*length = propNameLength + propDataLength + 2;
1989 
1990 	return true;
1991 }
1992 
1993 
1994 UInt16
1995 IODTNVRAM::generateOWChecksum(UInt8 *buffer)
1996 {
1997 	UInt32 cnt, checksum = 0;
1998 	UInt16 *tmpBuffer = (UInt16 *)buffer;
1999 
2000 	for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
2001 		checksum += tmpBuffer[cnt];
2002 	}
2003 
2004 	return checksum % 0x0000FFFF;
2005 }
2006 
2007 bool
2008 IODTNVRAM::validateOWChecksum(UInt8 *buffer)
2009 {
2010 	UInt32 cnt, checksum, sum = 0;
2011 	UInt16 *tmpBuffer = (UInt16 *)buffer;
2012 
2013 	for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
2014 		sum += tmpBuffer[cnt];
2015 	}
2016 
2017 	checksum = (sum >> 16) + (sum & 0x0000FFFF);
2018 	if (checksum == 0x10000) {
2019 		checksum--;
2020 	}
2021 	checksum = (checksum ^ 0x0000FFFF) & 0x0000FFFF;
2022 
2023 	return checksum == 0;
2024 }
2025 
2026 void
2027 IODTNVRAM::updateOWBootArgs(const OSSymbol *key, OSObject *value)
2028 {
2029 	/* UNSUPPORTED */
2030 }
2031 
2032 bool
2033 IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor *hdr, UInt32 *where)
2034 {
2035 	return false;
2036 }
2037 
2038 IOReturn
2039 IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry *entry,
2040     const OSSymbol **name,
2041     OSData **value)
2042 {
2043 	return kIOReturnUnsupported;
2044 }
2045 
2046 
2047 IOReturn
2048 IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry *entry,
2049     const OSSymbol *name,
2050     OSData *value)
2051 {
2052 	return kIOReturnUnsupported;
2053 }
2054 
2055 
2056 OSSharedPtr<OSData>
2057 IODTNVRAM::unescapeBytesToData(const UInt8 *bytes, UInt32 length)
2058 {
2059 	OSSharedPtr<OSData> data;
2060 	UInt32              totalLength = 0;
2061 	UInt32              cnt, cnt2;
2062 	UInt8               byte;
2063 	bool                ok;
2064 
2065 	// Calculate the actual length of the data.
2066 	ok = true;
2067 	totalLength = 0;
2068 	for (cnt = 0; cnt < length;) {
2069 		byte = bytes[cnt++];
2070 		if (byte == 0xFF) {
2071 			byte = bytes[cnt++];
2072 			if (byte == 0x00) {
2073 				ok = false;
2074 				break;
2075 			}
2076 			cnt2 = byte & 0x7F;
2077 		} else {
2078 			cnt2 = 1;
2079 		}
2080 		totalLength += cnt2;
2081 	}
2082 
2083 	if (ok) {
2084 		// Create an empty OSData of the correct size.
2085 		data = OSData::withCapacity(totalLength);
2086 		if (data != nullptr) {
2087 			for (cnt = 0; cnt < length;) {
2088 				byte = bytes[cnt++];
2089 				if (byte == 0xFF) {
2090 					byte = bytes[cnt++];
2091 					cnt2 = byte & 0x7F;
2092 					byte = (byte & 0x80) ? 0xFF : 0x00;
2093 				} else {
2094 					cnt2 = 1;
2095 				}
2096 				data->appendByte(byte, cnt2);
2097 			}
2098 		}
2099 	}
2100 
2101 	return data;
2102 }
2103 
2104 OSSharedPtr<OSData>
2105 IODTNVRAM::escapeDataToData(OSData * value)
2106 {
2107 	OSSharedPtr<OSData> result;
2108 	const UInt8         *startPtr;
2109 	const UInt8         *endPtr;
2110 	const UInt8         *wherePtr;
2111 	UInt8               byte;
2112 	bool                ok = true;
2113 
2114 	wherePtr = (const UInt8 *) value->getBytesNoCopy();
2115 	endPtr = wherePtr + value->getLength();
2116 
2117 	result = OSData::withCapacity((unsigned int) (endPtr - wherePtr));
2118 	if (!result) {
2119 		return result;
2120 	}
2121 
2122 	while (wherePtr < endPtr) {
2123 		startPtr = wherePtr;
2124 		byte = *wherePtr++;
2125 		if ((byte == 0x00) || (byte == 0xFF)) {
2126 			for (;
2127 			    ((wherePtr - startPtr) < 0x80) && (wherePtr < endPtr) && (byte == *wherePtr);
2128 			    wherePtr++) {
2129 			}
2130 			ok &= result->appendByte(0xff, 1);
2131 			byte = (byte & 0x80) | ((UInt8)(wherePtr - startPtr));
2132 		}
2133 		ok &= result->appendByte(byte, 1);
2134 	}
2135 	ok &= result->appendByte(0, 1);
2136 
2137 	if (!ok) {
2138 		result.reset();
2139 	}
2140 
2141 	return result;
2142 }
2143 
2144 static bool
2145 IsApplePropertyName(const char * propName)
2146 {
2147 	char c;
2148 	while ((c = *propName++)) {
2149 		if ((c >= 'A') && (c <= 'Z')) {
2150 			break;
2151 		}
2152 	}
2153 
2154 	return c == 0;
2155 }
2156 
2157 IOReturn
2158 IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry *entry,
2159     const OSSymbol **name,
2160     OSData **value)
2161 {
2162 	IOReturn    err = kIOReturnNoResources;
2163 	OSData      *data;
2164 	const UInt8 *startPtr;
2165 	const UInt8 *endPtr;
2166 	const UInt8 *wherePtr;
2167 	const UInt8 *nvPath = nullptr;
2168 	const char  *nvName = nullptr;
2169 	const char  *resultName = nullptr;
2170 	const UInt8 *resultValue = nullptr;
2171 	UInt32       resultValueLen = 0;
2172 	UInt8       byte;
2173 
2174 	NVRAMLOCK();
2175 	data = OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get()));
2176 	NVRAMUNLOCK();
2177 
2178 	if (data == nullptr) {
2179 		return err;
2180 	}
2181 
2182 	startPtr = (const UInt8 *) data->getBytesNoCopy();
2183 	endPtr = startPtr + data->getLength();
2184 
2185 	wherePtr = startPtr;
2186 	while (wherePtr < endPtr) {
2187 		byte = *(wherePtr++);
2188 		if (byte) {
2189 			continue;
2190 		}
2191 
2192 		if (nvPath == nullptr) {
2193 			nvPath = startPtr;
2194 		} else if (nvName == nullptr) {
2195 			nvName = (const char *) startPtr;
2196 		} else {
2197 			OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
2198 			if (entry == compareEntry) {
2199 				bool appleProp = IsApplePropertyName(nvName);
2200 				if (!appleProp || !resultName) {
2201 					resultName     = nvName;
2202 					resultValue    = startPtr;
2203 					resultValueLen = (UInt32) (wherePtr - startPtr - 1);  // OSData getLength() is 32b
2204 				}
2205 				if (!appleProp) {
2206 					break;
2207 				}
2208 			}
2209 			nvPath = nullptr;
2210 			nvName = nullptr;
2211 		}
2212 		startPtr = wherePtr;
2213 	}
2214 	if (resultName) {
2215 		*name = OSSymbol::withCString(resultName).detach();
2216 		*value = unescapeBytesToData(resultValue, resultValueLen).detach();
2217 		if ((*name != nullptr) && (*value != nullptr)) {
2218 			err = kIOReturnSuccess;
2219 		} else {
2220 			err = kIOReturnNoMemory;
2221 		}
2222 	}
2223 	return err;
2224 }
2225 
2226 IOReturn
2227 IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
2228     const OSSymbol *propName,
2229     OSData *value)
2230 {
2231 	OSSharedPtr<OSData> data, oldData;
2232 	const UInt8         *startPtr;
2233 	const UInt8         *propStart;
2234 	const UInt8         *endPtr;
2235 	const UInt8         *wherePtr;
2236 	const UInt8         *nvPath = nullptr;
2237 	const char          *nvName = nullptr;
2238 	const char          *comp;
2239 	const char          *name;
2240 	UInt8               byte;
2241 	bool                ok = true;
2242 	bool                settingAppleProp;
2243 
2244 	settingAppleProp = IsApplePropertyName(propName->getCStringNoCopy());
2245 
2246 	// copy over existing properties for other entries
2247 
2248 	NVRAMLOCK();
2249 
2250 	oldData.reset(OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get())), OSRetain);
2251 	if (oldData) {
2252 		startPtr = (const UInt8 *) oldData->getBytesNoCopy();
2253 		endPtr = startPtr + oldData->getLength();
2254 
2255 		propStart = startPtr;
2256 		wherePtr = startPtr;
2257 		while (wherePtr < endPtr) {
2258 			byte = *(wherePtr++);
2259 			if (byte) {
2260 				continue;
2261 			}
2262 			if (nvPath == nullptr) {
2263 				nvPath = startPtr;
2264 			} else if (nvName == nullptr) {
2265 				nvName = (const char *) startPtr;
2266 			} else {
2267 				OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
2268 
2269 				if (entry == compareEntry) {
2270 					if ((settingAppleProp && propName->isEqualTo(nvName))
2271 					    || (!settingAppleProp && !IsApplePropertyName(nvName))) {
2272 						// delete old property (nvPath -> wherePtr) source OSData len is 32b
2273 						data = OSData::withBytes(propStart, (UInt32)(nvPath - propStart));
2274 						if (data) {
2275 							ok &= data->appendBytes(wherePtr, (UInt32)(endPtr - wherePtr));
2276 						}
2277 						break;
2278 					}
2279 				}
2280 				nvPath = nullptr;
2281 				nvName = nullptr;
2282 			}
2283 
2284 			startPtr = wherePtr;
2285 		}
2286 	}
2287 
2288 	// make the new property
2289 
2290 	if (!data) {
2291 		if (oldData) {
2292 			data = OSData::withData(oldData.get());
2293 		} else {
2294 			data = OSData::withCapacity(16);
2295 		}
2296 		if (!data) {
2297 			ok = false;
2298 		}
2299 	}
2300 
2301 	if (ok && value && value->getLength()) {
2302 		do {
2303 			// get entries in path
2304 			OSSharedPtr<OSArray> array = OSArray::withCapacity(5);
2305 			if (!array) {
2306 				ok = false;
2307 				break;
2308 			}
2309 			do{
2310 				array->setObject(entry);
2311 			} while ((entry = entry->getParentEntry(gIODTPlane)));
2312 
2313 			// append path
2314 			for (int i = array->getCount() - 3;
2315 			    (entry = (IORegistryEntry *) array->getObject(i));
2316 			    i--) {
2317 				name = entry->getName(gIODTPlane);
2318 				comp = entry->getLocation(gIODTPlane);
2319 				if (comp) {
2320 					ok &= data->appendBytes("/@", 2);
2321 				} else {
2322 					if (!name) {
2323 						continue;
2324 					}
2325 					ok &= data->appendByte('/', 1);
2326 					comp = name;
2327 				}
2328 				ok &= data->appendBytes(comp, (unsigned int) strnlen(comp, UINT16_MAX));
2329 			}
2330 			ok &= data->appendByte(0, 1);
2331 			// append prop name
2332 			ok &= data->appendBytes(propName->getCStringNoCopy(), propName->getLength() + 1);
2333 
2334 			// append escaped data
2335 			OSSharedPtr<OSData> escapedData = escapeDataToData(value);
2336 			ok &= (escapedData != nullptr);
2337 			if (ok) {
2338 				ok &= data->appendBytes(escapedData.get());
2339 			}
2340 		} while (false);
2341 	}
2342 
2343 	if (ok) {
2344 		ok = _commonDict->setObject(_registryPropertiesKey.get(), data.get());
2345 	}
2346 
2347 	if (ok) {
2348 		if (syncVariables() != kIOReturnSuccess) {
2349 			if (oldData) {
2350 				_commonDict->setObject(_registryPropertiesKey.get(), oldData.get());
2351 			} else {
2352 				_commonDict->removeObject(_registryPropertiesKey.get());
2353 			}
2354 			(void) syncVariables();
2355 			ok = false;
2356 		}
2357 	}
2358 
2359 	oldData.reset();
2360 
2361 	NVRAMUNLOCK();
2362 
2363 	return ok ? kIOReturnSuccess : kIOReturnNoMemory;
2364 }
2365 
2366 bool
2367 IODTNVRAM::safeToSync(void)
2368 {
2369 	AbsoluteTime delta;
2370 	UInt64       delta_ns;
2371 	SInt32       delta_secs;
2372 
2373 	// delta interval went by
2374 	clock_get_uptime(&delta);
2375 
2376 	// Figure it in seconds.
2377 	absolutetime_to_nanoseconds(delta, &delta_ns);
2378 	delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
2379 
2380 	if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
2381 		_lastDeviceSync = delta_secs;
2382 		_freshInterval = FALSE;
2383 		return TRUE;
2384 	}
2385 
2386 	return FALSE;
2387 }
2388