xref: /xnu-11215/iokit/Kernel/IONVRAM.cpp (revision aca3beaa)
1 /*
2  * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
3  * Copyright (c) 2007-2021 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 <os/system_event_log.h>
42 #include <sys/csr.h>
43 
44 #define super IOService
45 
46 OSDefineMetaClassAndStructors(IODTNVRAM, IOService);
47 
48 class IONVRAMCHRPHandler;
49 class IONVRAMV3Handler;
50 
51 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
52 
53 #define MAX_VAR_NAME_SIZE     63
54 
55 #define kNVRAMBankSizeKey    "nvram-bank-size"
56 #define kNVRAMBankCountKey   "nvram-bank-count"
57 #define kNVRAMCurrentBankKey "nvram-current-bank"
58 
59 #define kCurrentGenerationCountKey "Generation"
60 #define kCurrentNVRAMVersionKey    "Version"
61 
62 #define kNVRAMCommonUsedKey    "CommonUsed"
63 #define kNVRAMSystemUsedKey    "SystemUsed"
64 
65 #define kIONVRAMPrivilege       kIOClientPrivilegeAdministrator
66 
67 #define MIN_SYNC_NOW_INTERVAL 15*60 /* Minimum 15 Minutes interval mandated */
68 
69 #if defined(DEBUG) || defined(DEVELOPMENT)
70 #define DEBUG_IFERROR(err, fmt, args...)                                     \
71 ({                                                                           \
72 	if ((err != kIOReturnSuccess) || gNVRAMLogging)                          \
73 	IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
74 })
75 
76 #define DEBUG_INFO(fmt, args...)                                             \
77 ({                                                                           \
78 	if (gNVRAMLogging)                                                       \
79 	IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
80 })
81 
82 #define DEBUG_ALWAYS(fmt, args...)                                           \
83 ({                                                                           \
84 	IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
85 })
86 #else
87 #define DEBUG_IFERROR(err, fmt, args...) (void)NULL
88 #define DEBUG_INFO(fmt, args...) (void)NULL
89 #define DEBUG_ALWAYS(fmt, args...) (void)NULL
90 #endif
91 
92 #define DEBUG_ERROR DEBUG_ALWAYS
93 
94 #define SAFE_TO_LOCK() (preemption_enabled() && !panic_active())
95 
96 #define CONTROLLERLOCK()                     \
97 ({                                           \
98 	if (SAFE_TO_LOCK())                  \
99 	        IOLockLock(_controllerLock); \
100 })
101 
102 #define CONTROLLERUNLOCK()                     \
103 ({                                             \
104 	if (SAFE_TO_LOCK())                    \
105 	        IOLockUnlock(_controllerLock); \
106 })
107 
108 #define NVRAMREADLOCK()                       \
109 ({                                            \
110 	if (SAFE_TO_LOCK())                   \
111 	        IORWLockRead(_variableLock);  \
112 })
113 
114 #define NVRAMWRITELOCK()                      \
115 ({                                            \
116 	if (SAFE_TO_LOCK())                   \
117 	        IORWLockWrite(_variableLock); \
118 })
119 
120 #define NVRAMUNLOCK()                          \
121 ({                                             \
122 	if (SAFE_TO_LOCK())                    \
123 	        IORWLockUnlock(_variableLock); \
124 })
125 
126 #define NVRAMLOCKASSERTHELD()                                       \
127 ({                                                                  \
128 	if (SAFE_TO_LOCK())                                         \
129 	        IORWLockAssert(_variableLock, kIORWLockAssertHeld); \
130 })
131 
132 #define NVRAMLOCKASSERTEXCLUSIVE()                                   \
133 ({                                                                   \
134 	if (SAFE_TO_LOCK())                                          \
135 	        IORWLockAssert(_variableLock, kIORWLockAssertWrite); \
136 })
137 
138 #define ENTITLED_MODIFY_ONLY ((1 << kIONVRAMOperationWrite) | (1 << kIONVRAMOperationDelete) | (1 << kIONVRAMOperationObliterate) | (1 << kIONVRAMOperationReset))
139 
140 enum NVRAMVersion {
141 	kNVRAMVersionUnknown,
142 	kNVRAMVersion1,       // Legacy, banks, 0x800 common partition size
143 	kNVRAMVersion2,       // V1 but with (0x2000 - sizeof(struct apple_nvram_header) - sizeof(struct chrp_nvram_header)) common region
144 	kNVRAMVersion3,       // New EFI based format
145 	kNVRAMVersionMax
146 };
147 
148 // Guid for Apple System Boot variables
149 // 40A0DDD2-77F8-4392-B4A3-1E7304206516
150 UUID_DEFINE(gAppleSystemVariableGuid, 0x40, 0xA0, 0xDD, 0xD2, 0x77, 0xF8, 0x43, 0x92, 0xB4, 0xA3, 0x1E, 0x73, 0x04, 0x20, 0x65, 0x16);
151 
152 // Apple NVRAM Variable namespace (APPLE_VENDOR_OS_VARIABLE_GUID)
153 // 7C436110-AB2A-4BBB-A880-FE41995C9F82
154 UUID_DEFINE(gAppleNVRAMGuid, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82);
155 
156 static TUNABLE(bool, gNVRAMLogging, "nvram-log", false);
157 static bool gInternalBuild = false;
158 
159 // allowlist variables from macboot that need to be set/get from system region if present
160 static const char * const gNVRAMSystemList[] = { IONVRAMSystemVariableList, nullptr };
161 
162 typedef struct {
163 	const char *name;
164 	IONVRAMVariableType type;
165 } VariableTypeEntry;
166 
167 static const
168 VariableTypeEntry gVariableTypes[] = {
169 	{"auto-boot?", kOFVariableTypeBoolean},
170 	{"boot-args", kOFVariableTypeString},
171 	{"boot-command", kOFVariableTypeString},
172 	{"boot-device", kOFVariableTypeString},
173 	{"boot-file", kOFVariableTypeString},
174 	{"boot-screen", kOFVariableTypeString},
175 	{"boot-script", kOFVariableTypeString},
176 	{"console-screen", kOFVariableTypeString},
177 	{"default-client-ip", kOFVariableTypeString},
178 	{"default-gateway-ip", kOFVariableTypeString},
179 	{"default-mac-address?", kOFVariableTypeBoolean},
180 	{"default-router-ip", kOFVariableTypeString},
181 	{"default-server-ip", kOFVariableTypeString},
182 	{"default-subnet-mask", kOFVariableTypeString},
183 	{"diag-device", kOFVariableTypeString},
184 	{"diag-file", kOFVariableTypeString},
185 	{"diag-switch?", kOFVariableTypeBoolean},
186 	{"fcode-debug?", kOFVariableTypeBoolean},
187 	{"input-device", kOFVariableTypeString},
188 	{"input-device-1", kOFVariableTypeString},
189 	{"little-endian?", kOFVariableTypeBoolean},
190 	{"load-base", kOFVariableTypeNumber},
191 	{"mouse-device", kOFVariableTypeString},
192 	{"nvramrc", kOFVariableTypeString},
193 	{"oem-banner", kOFVariableTypeString},
194 	{"oem-banner?", kOFVariableTypeBoolean},
195 	{"oem-logo", kOFVariableTypeString},
196 	{"oem-logo?", kOFVariableTypeBoolean},
197 	{"output-device", kOFVariableTypeString},
198 	{"output-device-1", kOFVariableTypeString},
199 	{"pci-probe-list", kOFVariableTypeNumber},
200 	{"pci-probe-mask", kOFVariableTypeNumber},
201 	{"real-base", kOFVariableTypeNumber},
202 	{"real-mode?", kOFVariableTypeBoolean},
203 	{"real-size", kOFVariableTypeNumber},
204 	{"screen-#columns", kOFVariableTypeNumber},
205 	{"screen-#rows", kOFVariableTypeNumber},
206 	{"security-mode", kOFVariableTypeString},
207 	{"selftest-#megs", kOFVariableTypeNumber},
208 	{"use-generic?", kOFVariableTypeBoolean},
209 	{"use-nvramrc?", kOFVariableTypeBoolean},
210 	{"virt-base", kOFVariableTypeNumber},
211 	{"virt-size", kOFVariableTypeNumber},
212 
213 #if !defined(__x86_64__)
214 	{"acc-cm-override-charger-count", kOFVariableTypeNumber},
215 	{"acc-cm-override-count", kOFVariableTypeNumber},
216 	{"acc-mb-ld-lifetime", kOFVariableTypeNumber},
217 	{"com.apple.System.boot-nonce", kOFVariableTypeString},
218 	{"darkboot", kOFVariableTypeBoolean},
219 	{"enter-tdm-mode", kOFVariableTypeBoolean},
220 #endif /* !defined(__x86_64__) */
221 	{nullptr, kOFVariableTypeData} // Default type to return
222 };
223 
224 union VariablePermission {
225 	struct {
226 		uint64_t UserWrite            :1;
227 		uint64_t RootRequired         :1;
228 		uint64_t KernelOnly           :1;
229 		uint64_t ResetNVRAMOnlyDelete :1;
230 		uint64_t NeverAllowedToDelete :1;
231 		uint64_t SystemReadHidden     :1;
232 		uint64_t FullAccess           :1;
233 		uint64_t Reserved:57;
234 	} Bits;
235 	uint64_t Uint64;
236 };
237 
238 typedef struct {
239 	const char *name;
240 	VariablePermission p;
241 } VariablePermissionEntry;
242 
243 static const
244 VariablePermissionEntry gVariablePermissions[] = {
245 	{"aapl,pci", .p.Bits.RootRequired = 1},
246 	{"battery-health", .p.Bits.RootRequired = 1,
247 	 .p.Bits.NeverAllowedToDelete = 1},
248 	{"boot-image", .p.Bits.UserWrite = 1},
249 	{"com.apple.System.fp-state", .p.Bits.KernelOnly = 1},
250 	{"fm-account-masked", .p.Bits.RootRequired = 1,
251 	 .p.Bits.NeverAllowedToDelete = 1},
252 	{"fm-activation-locked", .p.Bits.RootRequired = 1,
253 	 .p.Bits.NeverAllowedToDelete = 1},
254 	{"fm-spkeys", .p.Bits.RootRequired = 1,
255 	 .p.Bits.NeverAllowedToDelete = 1},
256 	{"fm-spstatus", .p.Bits.RootRequired = 1,
257 	 .p.Bits.NeverAllowedToDelete = 1},
258 	{"policy-nonce-digests", .p.Bits.ResetNVRAMOnlyDelete = 1}, // Deleting this via user triggered obliterate leave J273a unable to boot
259 	{"recoveryos-passcode-blob", .p.Bits.SystemReadHidden = 1},
260 	{"security-password", .p.Bits.RootRequired = 1},
261 	{"system-passcode-lock-blob", .p.Bits.SystemReadHidden = 1},
262 
263 #if !defined(__x86_64__)
264 	{"acc-cm-override-charger-count", .p.Bits.KernelOnly = 1},
265 	{"acc-cm-override-count", .p.Bits.KernelOnly = 1},
266 	{"acc-mb-ld-lifetime", .p.Bits.KernelOnly = 1},
267 	{"backlight-level", .p.Bits.UserWrite = 1},
268 	{"backlight-nits", .p.Bits.UserWrite = 1},
269 	{"com.apple.System.boot-nonce", .p.Bits.KernelOnly = 1},
270 	{"com.apple.System.sep.art", .p.Bits.KernelOnly = 1},
271 	{"darkboot", .p.Bits.UserWrite = 1},
272 	{"nonce-seeds", .p.Bits.KernelOnly = 1},
273 #endif /* !defined(__x86_64__) */
274 	// Variables used for testing permissions
275 	{"testSysReadHidden", .p.Bits.SystemReadHidden = 1},
276 	{"testKernelOnly", .p.Bits.KernelOnly = 1},
277 	{"testResetOnlyDel", .p.Bits.ResetNVRAMOnlyDelete = 1},
278 	{"testNeverDel", .p.Bits.NeverAllowedToDelete = 1},
279 	{"testUserWrite", .p.Bits.UserWrite = 1},
280 	{"testRootReq", .p.Bits.RootRequired = 1},
281 	{nullptr, {.Bits.FullAccess = 1}} // Default access
282 };
283 
284 typedef struct {
285 	const uint8_t checkOp;
286 	const uuid_t  *varGuid;
287 	const char    *varName;
288 	const char    *varEntitlement;
289 } VariableEntitlementEntry;
290 
291 // variable-guid pair entries that require entitlement check to do specified nvram operations
292 static const
293 VariableEntitlementEntry gVariableEntitlements[] = {
294 	{ENTITLED_MODIFY_ONLY, &gAppleNVRAMGuid, "ownership-warning", "com.apple.private.iokit.ddl-write"},
295 	// Variable used for testing entitlement
296 	{ENTITLED_MODIFY_ONLY, &gAppleNVRAMGuid, "testEntitlement", "com.apple.private.iokit.testentitlement"},
297 	{0, &UUID_NULL, nullptr, nullptr}
298 };
299 
300 static NVRAMPartitionType
301 getPartitionTypeForGUID(const uuid_t guid)
302 {
303 	if (uuid_compare(guid, gAppleSystemVariableGuid) == 0) {
304 		return kIONVRAMPartitionSystem;
305 	} else {
306 		return kIONVRAMPartitionCommon;
307 	}
308 }
309 
310 static IONVRAMVariableType
311 getVariableType(const char *propName)
312 {
313 	const VariableTypeEntry *entry;
314 
315 	entry = gVariableTypes;
316 	while (entry->name != nullptr) {
317 		if (strcmp(entry->name, propName) == 0) {
318 			break;
319 		}
320 		entry++;
321 	}
322 
323 	return entry->type;
324 }
325 
326 static IONVRAMVariableType
327 getVariableType(const OSSymbol *propSymbol)
328 {
329 	return getVariableType(propSymbol->getCStringNoCopy());
330 }
331 
332 static VariablePermission
333 getVariablePermission(const char *propName)
334 {
335 	const VariablePermissionEntry *entry;
336 
337 	entry = gVariablePermissions;
338 	while (entry->name != nullptr) {
339 		if (strcmp(entry->name, propName) == 0) {
340 			break;
341 		}
342 		entry++;
343 	}
344 
345 	return entry->p;
346 }
347 
348 static bool
349 variableInAllowList(const char *varName)
350 {
351 	unsigned int i = 0;
352 
353 	while (gNVRAMSystemList[i] != nullptr) {
354 		if (strcmp(varName, gNVRAMSystemList[i]) == 0) {
355 			return true;
356 		}
357 		i++;
358 	}
359 
360 	return false;
361 }
362 
363 static bool
364 verifyWriteSizeLimit(const uuid_t varGuid, const char *variableName, size_t propDataSize)
365 {
366 	if (variableInAllowList(variableName)) {
367 		if (strnstr(variableName, "breadcrumbs", strlen(variableName)) != NULL) {
368 			return propDataSize <= 1024;
369 		} else {
370 			return propDataSize <= 768;
371 		}
372 	}
373 
374 	return true;
375 }
376 
377 #if defined(DEBUG) || defined(DEVELOPMENT)
378 static const char *
379 getNVRAMOpString(IONVRAMOperation op)
380 {
381 	switch (op) {
382 	case kIONVRAMOperationRead:
383 		return "Read";
384 	case kIONVRAMOperationWrite:
385 		return "Write";
386 	case kIONVRAMOperationDelete:
387 		return "Delete";
388 	case kIONVRAMOperationObliterate:
389 		return "Obliterate";
390 	case kIONVRAMOperationReset:
391 		return "Reset";
392 	case kIONVRAMOperationInit:
393 		return "Init";
394 	default:
395 		return "Unknown";
396 	}
397 }
398 #endif
399 
400 /*
401  * Parse a variable name of the form "GUID:name".
402  * If the name cannot be parsed, substitute the Apple global variable GUID.
403  * Returns TRUE if a GUID was found in the name, FALSE otherwise.
404  * The guidResult and nameResult arguments may be nullptr if you just want
405  * to check the format of the string.
406  */
407 static bool
408 parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult)
409 {
410 	uuid_string_t temp    = {0};
411 	size_t        keyLen  = strlen(key);
412 	bool          ok      = false;
413 	const char    *name   = key;
414 	uuid_t        guid;
415 
416 	if (keyLen > sizeof(temp)) {
417 		// check for at least UUID + ":" + more
418 		memcpy(temp, key, sizeof(temp) - 1);
419 
420 		if ((uuid_parse(temp, guid) == 0) &&
421 		    (key[sizeof(temp) - 1] == ':')) {
422 			name = key + sizeof(temp);
423 			ok     = true;
424 		}
425 	}
426 
427 	if (guidResult) {
428 		ok ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
429 	}
430 	if (nameResult) {
431 		*nameResult = name;
432 	}
433 
434 	return ok;
435 }
436 
437 static bool
438 parseVariableName(const OSSymbol *key, uuid_t *guidResult, const char **nameResult)
439 {
440 	return parseVariableName(key->getCStringNoCopy(), guidResult, nameResult);
441 }
442 
443 /**
444  * @brief Translates(if needed) varGuid and stores it in destGuid
445  *
446  * @param varGuid       guid to translate
447  * @param variableName  variable name attached to the guid
448  * @param destGuid      translated guid is saved here
449  * @param systemActive  boolean to indicate if it has system partition size > 0
450  */
451 static void
452 translateGUID(const uuid_t varGuid, const char *variableName, uuid_t destGuid, bool systemActive)
453 {
454 	if (varGuid == nullptr || variableName == nullptr || destGuid == nullptr) {
455 		DEBUG_ERROR("nullptr passed as an argument\n");
456 		return;
457 	}
458 
459 	bool systemGuid = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
460 
461 	if (systemActive) {
462 		if (variableInAllowList(variableName)) {
463 			if (!systemGuid) {
464 				DEBUG_ERROR("System GUID not supplied for %s\n", variableName);
465 			}
466 			DEBUG_INFO("Using system GUID due to allow list\n");
467 			uuid_copy(destGuid, gAppleSystemVariableGuid);
468 		} else if (systemGuid) {
469 			DEBUG_INFO("System GUID used\n");
470 			uuid_copy(destGuid, gAppleSystemVariableGuid);
471 		} else {
472 			DEBUG_INFO("Use given guid\n");
473 			uuid_copy(destGuid, varGuid);
474 		}
475 	} else if (systemGuid) {
476 		DEBUG_INFO("Overriding to Apple guid\n");
477 		uuid_copy(destGuid, gAppleNVRAMGuid);
478 	} else {
479 		DEBUG_INFO("Use given guid\n");
480 		uuid_copy(destGuid, varGuid);
481 	}
482 }
483 
484 /**
485  * @brief Checks if the variable-guid(translated) pair is present in gVariableEntitlements and if so,
486  *        does it have the required entitlement for the NVRAM operation passed in
487  *
488  * @param varGuid       guid for the variable to be checked, this gets translated by translateGUID
489  * @param varName       variable name
490  * @param op            NVRAM operation
491  * @param systemActive  used to pass into translateGUID to get the correct guid to check against
492  * @param veChecked     if variable entitlement is checked, this is set to true
493  * @return true         if variable wasn't present in gVariableEntitlements,
494  *                      entitlement check wasn't required for operation passed in,
495  *                      or if entitlement check returned true
496  * @return false        if varName/varGuid/veChecked was NULL or if entitlement check returned false
497  */
498 static bool
499 verifyVarEntitlement(const uuid_t varGuid, const char *varName, IONVRAMOperation op, bool systemActive, bool *veChecked)
500 {
501 	if (varGuid == nullptr || varName == nullptr || veChecked == nullptr) {
502 		DEBUG_ERROR("nullptr passed as an argument\n");
503 		return false;
504 	}
505 
506 	uuid_t translatedGuid;
507 	const VariableEntitlementEntry *entry;
508 	*veChecked = false;
509 
510 	entry = gVariableEntitlements;
511 	while ((entry != nullptr) && (entry->varName != nullptr)) {
512 		translateGUID(varGuid, varName, translatedGuid, systemActive);
513 		if ((strcmp(entry->varName, varName) == 0) && (uuid_compare(translatedGuid, *(entry->varGuid)) == 0)) {
514 			// check if task entitlement check is required for this operation
515 			if (entry->checkOp & (1 << op)) {
516 				*veChecked = true;
517 				return IOCurrentTaskHasEntitlement(entry->varEntitlement);
518 			}
519 			break;
520 		}
521 		entry++;
522 	}
523 
524 	return true;
525 }
526 
527 static bool
528 verifyPermission(IONVRAMOperation op, const uuid_t varGuid, const char *varName, const bool systemActive)
529 {
530 	VariablePermission perm;
531 	bool kernel, varEntitled, writeEntitled = false, readEntitled = false, allowList, systemGuid = false, systemEntitled = false, systemInternalEntitled = false, systemAllow, systemReadHiddenAllow = false;
532 	bool admin = false;
533 	bool ok = false;
534 
535 	if (verifyVarEntitlement(varGuid, varName, op, systemActive, &varEntitled) == false) {
536 		goto exit;
537 	}
538 
539 	perm = getVariablePermission(varName);
540 
541 	kernel = current_task() == kernel_task;
542 
543 	if (perm.Bits.KernelOnly) {
544 		DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
545 		ok = kernel;
546 		goto exit;
547 	}
548 
549 	allowList              = variableInAllowList(varName);
550 	systemGuid             = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
551 	admin                  = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess;
552 	writeEntitled          = IOCurrentTaskHasEntitlement(kIONVRAMWriteAccessKey);
553 	readEntitled           = IOCurrentTaskHasEntitlement(kIONVRAMReadAccessKey);
554 	systemEntitled         = IOCurrentTaskHasEntitlement(kIONVRAMSystemAllowKey);
555 	systemInternalEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemInternalAllowKey);
556 	systemReadHiddenAllow  = IOCurrentTaskHasEntitlement(kIONVRAMSystemHiddenAllowKey);
557 
558 	systemAllow = systemEntitled || (systemInternalEntitled && gInternalBuild) || kernel;
559 
560 	switch (op) {
561 	case kIONVRAMOperationRead:
562 		if (systemGuid && perm.Bits.SystemReadHidden) {
563 			ok = systemReadHiddenAllow;
564 		} else if (kernel || admin || readEntitled || perm.Bits.FullAccess) {
565 			ok = true;
566 		}
567 		break;
568 
569 	case kIONVRAMOperationWrite:
570 		if (kernel || perm.Bits.UserWrite || admin || writeEntitled || varEntitled) {
571 			if (systemGuid) {
572 				if (allowList) {
573 					if (!systemAllow) {
574 						DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
575 					}
576 				} else if (!systemAllow) {
577 					DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
578 					break;
579 				}
580 			}
581 			ok = true;
582 		}
583 		break;
584 
585 	case kIONVRAMOperationDelete:
586 	case kIONVRAMOperationObliterate:
587 	case kIONVRAMOperationReset:
588 		if (perm.Bits.NeverAllowedToDelete) {
589 			DEBUG_INFO("Never allowed to delete %s\n", varName);
590 			break;
591 		} else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) {
592 			DEBUG_INFO("Not allowed to obliterate %s\n", varName);
593 			break;
594 		} else if ((op == kIONVRAMOperationDelete) && perm.Bits.ResetNVRAMOnlyDelete) {
595 			DEBUG_INFO("Only allowed to delete %s via NVRAM reset\n", varName);
596 			break;
597 		}
598 
599 		if (kernel || perm.Bits.UserWrite || admin || writeEntitled || varEntitled) {
600 			if (systemGuid) {
601 				if (allowList) {
602 					if (!systemAllow) {
603 						DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
604 					}
605 				} else if (!systemAllow) {
606 					DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
607 					break;
608 				}
609 			}
610 			ok = true;
611 		}
612 		break;
613 
614 	case kIONVRAMOperationInit:
615 		break;
616 	}
617 
618 exit:
619 	DEBUG_INFO("Permission for %s of %s %s: kern=%d, adm=%d, wE=%d, rE=%d, sG=%d, sEd=%d, sIEd=%d, sRHA=%d, UW=%d, vE=%d\n", getNVRAMOpString(op), varName, ok ? "granted" : "denied",
620 	    kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled, systemInternalEntitled, systemReadHiddenAllow, perm.Bits.UserWrite, varEntitled);
621 
622 	return ok;
623 }
624 
625 static bool
626 verifyPermission(IONVRAMOperation op, const OSSymbol *canonicalKey, const bool systemActive)
627 {
628 	const char *varName;
629 	uuid_t varGuid;
630 
631 	parseVariableName(canonicalKey->getCStringNoCopy(), &varGuid, &varName);
632 
633 	return verifyPermission(op, varGuid, varName, systemActive);
634 }
635 
636 static bool
637 skipKey(const OSSymbol *aKey)
638 {
639 	return aKey->isEqualTo(kIORegistryEntryAllowableSetPropertiesKey) ||
640 	       aKey->isEqualTo(kIORegistryEntryDefaultLockingSetPropertiesKey) ||
641 	       aKey->isEqualTo(kIOClassNameOverrideKey) ||
642 	       aKey->isEqualTo(kIOBSDNameKey) ||
643 	       aKey->isEqualTo(kIOBSDNamesKey) ||
644 	       aKey->isEqualTo(kIOBSDMajorKey) ||
645 	       aKey->isEqualTo(kIOBSDMinorKey) ||
646 	       aKey->isEqualTo(kIOBSDUnitKey) ||
647 	       aKey->isEqualTo(kIOUserServicePropertiesKey) ||
648 	       aKey->isEqualTo(kIOMatchCategoryKey);
649 }
650 
651 static OSSharedPtr<const OSSymbol>
652 keyWithGuidAndCString(const uuid_t guid, const char * cstring)
653 {
654 	size_t                      length;
655 	OSSharedPtr<const OSSymbol> symbolObj;
656 	char                        *canonicalString;
657 
658 	length = sizeof(uuid_string_t) - 1 + sizeof(':') + strlen(cstring) + 1;
659 
660 	canonicalString = (char *) IOMallocData(length);
661 	if (canonicalString == nullptr) {
662 		return NULL;
663 	}
664 
665 	uuid_unparse(guid, *((uuid_string_t*)canonicalString));
666 	canonicalString[sizeof(uuid_string_t) - 1] = ':';
667 
668 	strlcpy(&canonicalString[sizeof(uuid_string_t)], cstring, length - sizeof(uuid_string_t));
669 
670 	symbolObj = OSSymbol::withCString(canonicalString);
671 	IOFreeData(canonicalString, length);
672 
673 	return symbolObj;
674 }
675 
676 static void
677 dumpDict(const OSDictionary *dict)
678 {
679 	const OSSymbol                    *key;
680 	OSSharedPtr<OSCollectionIterator> iter;
681 	unsigned int                      count = 0;
682 
683 	iter = OSCollectionIterator::withCollection(dict);
684 	if (iter == nullptr) {
685 		DEBUG_ERROR("failed to create iterator\n");
686 		goto exit;
687 	}
688 
689 	DEBUG_INFO("Dumping dict...\n");
690 	while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
691 		count++;
692 		DEBUG_INFO("%u: %s\n", count, key->getCStringNoCopy());
693 	}
694 
695 exit:
696 	return;
697 }
698 
699 // ************************** IODTNVRAMPlatformNotifier ****************************
700 // private IOService based class for passing notifications to IODTNVRAM
701 
702 class IODTNVRAMPlatformNotifier : public IOService
703 {
704 	OSDeclareDefaultStructors(IODTNVRAMPlatformNotifier)
705 private:
706 	IODTNVRAM *_provider;
707 
708 public:
709 	bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
710 
711 	virtual IOReturn callPlatformFunction( const OSSymbol * functionName,
712 	    bool waitForFunction,
713 	    void *param1, void *param2,
714 	    void *param3, void *param4 ) APPLE_KEXT_OVERRIDE;
715 };
716 
717 OSDefineMetaClassAndStructors(IODTNVRAMPlatformNotifier, IOService)
718 
719 bool
720 IODTNVRAMPlatformNotifier::start(IOService * provider)
721 {
722 	OSSharedPtr<OSSerializer> serializer;
723 	OSSharedPtr<OSNumber> value = OSNumber::withNumber(1000, 32);
724 
725 	_provider = OSDynamicCast(IODTNVRAM, provider);
726 	require(_provider != nullptr, error);
727 
728 	setProperty(gIOPlatformWakeActionKey, value.get());
729 
730 	require(super::start(provider), error);
731 
732 	registerService();
733 
734 	return true;
735 
736 error:
737 	stop(provider);
738 
739 	return false;
740 }
741 
742 #include <IOKit/IOHibernatePrivate.h>
743 #include <IOKit/pwr_mgt/RootDomain.h>
744 static const OSSharedPtr<const OSSymbol> gIOHibernateStateKey = OSSymbol::withCString(kIOHibernateStateKey);
745 
746 static uint32_t
747 hibernateState(void)
748 {
749 	OSSharedPtr<OSData> data = OSDynamicPtrCast<OSData>(IOService::getPMRootDomain()->copyProperty(gIOHibernateStateKey.get()->getCStringNoCopy()));
750 	uint32_t hibernateState = 0;
751 	if ((data != NULL) && (data->getLength() == sizeof(hibernateState))) {
752 		memcpy(&hibernateState, data->getBytesNoCopy(), sizeof(hibernateState));
753 	}
754 	return hibernateState;
755 }
756 
757 IOReturn
758 IODTNVRAMPlatformNotifier::callPlatformFunction( const OSSymbol * functionName,
759     bool waitForFunction,
760     void *param1, void *param2,
761     void *param3, void *param4 )
762 {
763 	if ((functionName == gIOPlatformWakeActionKey) &&
764 	    (hibernateState() == kIOHibernateStateWakingFromHibernate)) {
765 		DEBUG_INFO("waking from hibernate\n");
766 		_provider->reload();
767 		return kIOReturnSuccess;
768 	}
769 
770 	return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
771 }
772 
773 
774 // ************************** IODTNVRAMDiags ****************************
775 // private IOService based class for passing notifications to IODTNVRAM
776 #define kIODTNVRAMDiagsStatsKey   "Stats"
777 #define kIODTNVRAMDiagsInitKey    "Init"
778 #define kIODTNVRAMDiagsReadKey    "Read"
779 #define kIODTNVRAMDiagsWriteKey   "Write"
780 #define kIODTNVRAMDiagsDeleteKey  "Delete"
781 #define kIODTNVRAMDiagsNameKey    "Name"
782 #define kIODTNVRAMDiagsSizeKey    "Size"
783 #define kIODTNVRAMDiagsPresentKey "Present"
784 
785 // private IOService based class for publishing diagnostic info for IODTNVRAM
786 class IODTNVRAMDiags : public IOService
787 {
788 	OSDeclareDefaultStructors(IODTNVRAMDiags)
789 private:
790 	IODTNVRAM                 *_provider;
791 	IORWLock                  *_variableLock;
792 	OSSharedPtr<OSDictionary> _stats;
793 
794 	bool serializeStats(void *, OSSerialize * serializer);
795 
796 public:
797 	bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
798 	void logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data);
799 };
800 
801 OSDefineMetaClassAndStructors(IODTNVRAMDiags, IOService)
802 
803 bool
804 IODTNVRAMDiags::start(IOService * provider)
805 {
806 	OSSharedPtr<OSSerializer> serializer;
807 
808 	_provider = OSDynamicCast(IODTNVRAM, provider);
809 	require(_provider != nullptr, error);
810 
811 	require(super::start(provider), error);
812 
813 	_variableLock = IORWLockAlloc();
814 	require(_variableLock != nullptr, error);
815 
816 	_stats = OSDictionary::withCapacity(1);
817 	require(_stats != nullptr, error);
818 
819 	serializer = OSSerializer::forTarget(this, OSMemberFunctionCast(OSSerializerCallback, this, &IODTNVRAMDiags::serializeStats));
820 	require(serializer != nullptr, error);
821 
822 	setProperty(kIODTNVRAMDiagsStatsKey, serializer.get());
823 
824 	registerService();
825 
826 	return true;
827 
828 error:
829 	stop(provider);
830 
831 	return false;
832 }
833 
834 void
835 IODTNVRAMDiags::logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data)
836 {
837 	// "Stats"        : OSDictionary
838 	// - "XX:varName" : OSDictionary, XX is the region value prefix to distinguish which dictionary the variable is in
839 	//   - "Init"     : OSBoolean True/present if variable present at initialization
840 	//   - "Read"     : OSNumber count
841 	//   - "Write"    : OSNumber count
842 	//   - "Delete"   : OSNumber count
843 	//   - "Size"     : OSNumber size, latest size from either init or write
844 	//   - "Present"  : OSBoolean True/False if variable is present or not
845 	char                      *entryKey;
846 	size_t                    entryKeySize;
847 	OSSharedPtr<OSDictionary> existingEntry;
848 	OSSharedPtr<OSNumber>     currentCount;
849 	OSSharedPtr<OSNumber>     varSize;
850 	const char                *opCountKey = nullptr;
851 
852 	entryKeySize = strlen("XX:") + strlen(name) +  1;
853 	entryKey = IONewData(char, entryKeySize);
854 	require(entryKey, exit);
855 
856 	snprintf(entryKey, entryKeySize, "%02X:%s", region, name);
857 
858 	NVRAMWRITELOCK();
859 	existingEntry.reset(OSDynamicCast(OSDictionary, _stats->getObject(entryKey)), OSRetain);
860 
861 	if (existingEntry == nullptr) {
862 		existingEntry = OSDictionary::withCapacity(4);
863 	}
864 
865 	switch (op) {
866 	case kIONVRAMOperationRead:
867 		opCountKey = kIODTNVRAMDiagsReadKey;
868 		if (existingEntry->getObject(kIODTNVRAMDiagsPresentKey) == nullptr) {
869 			existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
870 		}
871 		break;
872 	case kIONVRAMOperationWrite:
873 		opCountKey = kIODTNVRAMDiagsWriteKey;
874 		varSize = OSNumber::withNumber((size_t)data, 64);
875 		existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
876 		existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
877 		break;
878 	case kIONVRAMOperationDelete:
879 	case kIONVRAMOperationObliterate:
880 	case kIONVRAMOperationReset:
881 		opCountKey = kIODTNVRAMDiagsDeleteKey;
882 		existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
883 		break;
884 	case kIONVRAMOperationInit:
885 		varSize = OSNumber::withNumber((size_t)data, 64);
886 		existingEntry->setObject(kIODTNVRAMDiagsInitKey, varSize);
887 		existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
888 		existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
889 		break;
890 	default:
891 		goto unlock;
892 	}
893 
894 	if (opCountKey) {
895 		currentCount.reset(OSDynamicCast(OSNumber, existingEntry->getObject(opCountKey)), OSRetain);
896 
897 		if (currentCount == nullptr) {
898 			currentCount = OSNumber::withNumber(1, 64);
899 		} else {
900 			currentCount->addValue(1);
901 		}
902 
903 		existingEntry->setObject(opCountKey, currentCount);
904 	}
905 
906 	_stats->setObject(entryKey, existingEntry);
907 
908 unlock:
909 	NVRAMUNLOCK();
910 
911 exit:
912 	IODeleteData(entryKey, char, entryKeySize);
913 
914 	return;
915 }
916 
917 bool
918 IODTNVRAMDiags::serializeStats(void *, OSSerialize * serializer)
919 {
920 	bool ok;
921 
922 	NVRAMREADLOCK();
923 	ok = _stats->serialize(serializer);
924 	NVRAMUNLOCK();
925 
926 	return ok;
927 }
928 
929 // ************************** IODTNVRAMVariables ****************************
930 
931 // private IOService based class for publishing distinct dictionary properties on
932 // for easy ioreg access since the serializeProperties call is overloaded and is used
933 // as variable access
934 class IODTNVRAMVariables : public IOService
935 {
936 	OSDeclareDefaultStructors(IODTNVRAMVariables)
937 private:
938 	IODTNVRAM        *_provider;
939 	uuid_t           _guid;
940 	bool             _systemActive;
941 
942 public:
943 	bool                    init(const uuid_t guid, const bool systemActive);
944 	virtual bool            start(IOService * provider) APPLE_KEXT_OVERRIDE;
945 
946 	virtual bool            serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
947 	virtual OSPtr<OSObject> copyProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
948 	virtual OSObject        *getProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
949 	virtual bool            setProperty(const OSSymbol *aKey, OSObject *anObject) APPLE_KEXT_OVERRIDE;
950 	virtual IOReturn        setProperties(OSObject *properties) APPLE_KEXT_OVERRIDE;
951 	virtual void            removeProperty(const OSSymbol *aKey) APPLE_KEXT_OVERRIDE;
952 };
953 
954 OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
955 
956 bool
957 IODTNVRAMVariables::init(const uuid_t guid, const bool systemActive)
958 {
959 	require(super::init(), fail);
960 
961 	uuid_copy(_guid, guid);
962 	_systemActive = systemActive;
963 
964 	return true;
965 
966 fail:
967 	return false;
968 }
969 
970 bool
971 IODTNVRAMVariables::start(IOService * provider)
972 {
973 	_provider = OSDynamicCast(IODTNVRAM, provider);
974 	if (_provider == nullptr) {
975 		goto error;
976 	}
977 
978 	if (!super::start(provider)) {
979 		goto error;
980 	}
981 
982 	registerService();
983 
984 	return true;
985 
986 error:
987 	stop(provider);
988 
989 	return false;
990 }
991 
992 bool
993 IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
994 {
995 	const OSSymbol                    *key;
996 	OSSharedPtr<OSDictionary>         dict;
997 	OSSharedPtr<OSCollectionIterator> iter;
998 	OSSharedPtr<OSDictionary>         localVariables = _provider->_varDict;
999 	bool                              ok = false;
1000 
1001 	dict = OSDictionary::withCapacity(localVariables->getCount());
1002 	if (dict == nullptr) {
1003 		DEBUG_ERROR("No dictionary\n");
1004 		goto exit;
1005 	}
1006 
1007 	iter = OSCollectionIterator::withCollection(localVariables.get());
1008 	if (iter == nullptr) {
1009 		DEBUG_ERROR("failed to create iterator\n");
1010 		goto exit;
1011 	}
1012 
1013 	while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1014 		if (verifyPermission(kIONVRAMOperationRead, key, _systemActive)) {
1015 			uuid_t guid;
1016 			const char *name;
1017 
1018 			parseVariableName(key, &guid, &name);
1019 
1020 			if (uuid_compare(_guid, guid) == 0) {
1021 				OSSharedPtr<const OSSymbol> sym = OSSymbol::withCString(name);
1022 				dict->setObject(sym.get(), localVariables->getObject(key));
1023 			}
1024 		}
1025 	}
1026 
1027 	ok = dict->serialize(s);
1028 
1029 exit:
1030 	DEBUG_INFO("ok=%d\n", ok);
1031 	return ok;
1032 }
1033 
1034 OSPtr<OSObject>
1035 IODTNVRAMVariables::copyProperty(const OSSymbol *aKey) const
1036 {
1037 	if (_provider && !skipKey(aKey)) {
1038 		DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1039 
1040 		return _provider->copyPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
1041 	} else {
1042 		return nullptr;
1043 	}
1044 }
1045 
1046 OSObject *
1047 IODTNVRAMVariables::getProperty(const OSSymbol *aKey) const
1048 {
1049 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1050 
1051 	return theObject.get();
1052 }
1053 
1054 bool
1055 IODTNVRAMVariables::setProperty(const OSSymbol *aKey, OSObject *anObject)
1056 {
1057 	if (_provider) {
1058 		return _provider->setPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy(), anObject) == kIOReturnSuccess;
1059 	} else {
1060 		return false;
1061 	}
1062 }
1063 
1064 IOReturn
1065 IODTNVRAMVariables::setProperties(OSObject *properties)
1066 {
1067 	IOReturn                          ret = kIOReturnSuccess;
1068 	OSObject                          *object;
1069 	const OSSymbol                    *key;
1070 	OSDictionary                      *dict;
1071 	OSSharedPtr<OSCollectionIterator> iter;
1072 
1073 	dict = OSDynamicCast(OSDictionary, properties);
1074 	if (dict == nullptr) {
1075 		DEBUG_ERROR("Not a dictionary\n");
1076 		return kIOReturnBadArgument;
1077 	}
1078 
1079 	iter = OSCollectionIterator::withCollection(dict);
1080 	if (iter == nullptr) {
1081 		DEBUG_ERROR("Couldn't create iterator\n");
1082 		return kIOReturnBadArgument;
1083 	}
1084 
1085 	while (ret == kIOReturnSuccess) {
1086 		key = OSDynamicCast(OSSymbol, iter->getNextObject());
1087 		if (key == nullptr) {
1088 			break;
1089 		}
1090 
1091 		object = dict->getObject(key);
1092 		if (object == nullptr) {
1093 			continue;
1094 		}
1095 
1096 		ret = _provider->setPropertyWithGUIDAndName(_guid, key->getCStringNoCopy(), object);
1097 	}
1098 
1099 	DEBUG_INFO("ret=%#08x\n", ret);
1100 
1101 	return ret;
1102 }
1103 
1104 void
1105 IODTNVRAMVariables::removeProperty(const OSSymbol *aKey)
1106 {
1107 	_provider->removePropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
1108 }
1109 
1110 // ************************** Format Handlers ***************************
1111 class IODTNVRAMFormatHandler
1112 {
1113 protected:
1114 	uint32_t _bankSize;
1115 	uint32_t _bankCount;
1116 	uint32_t _currentBank;
1117 
1118 public:
1119 	virtual
1120 	~IODTNVRAMFormatHandler();
1121 	virtual bool     getNVRAMProperties(void);
1122 	virtual IOReturn unserializeVariables(void) = 0;
1123 	virtual IOReturn setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) = 0;
1124 	virtual bool     setController(IONVRAMController *_nvramController) = 0;
1125 	virtual bool     sync(void) = 0;
1126 	virtual IOReturn flush(const uuid_t guid, IONVRAMOperation op) = 0;
1127 	virtual void     reload(void) = 0;
1128 	virtual uint32_t getGeneration(void) const = 0;
1129 	virtual uint32_t getVersion(void) const = 0;
1130 	virtual uint32_t getSystemUsed(void) const = 0;
1131 	virtual uint32_t getCommonUsed(void) const = 0;
1132 	virtual bool     getSystemPartitionActive(void) const = 0;
1133 };
1134 
1135 IODTNVRAMFormatHandler::~IODTNVRAMFormatHandler()
1136 {
1137 }
1138 
1139 bool
1140 IODTNVRAMFormatHandler::getNVRAMProperties()
1141 {
1142 	bool                         ok    = false;
1143 	OSSharedPtr<IORegistryEntry> entry;
1144 	OSSharedPtr<OSObject>        prop;
1145 	OSData *                     data;
1146 
1147 	entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1148 	require_action(entry, exit, DEBUG_ERROR("Unable to find chosen node\n"));
1149 
1150 	prop = entry->copyProperty(kNVRAMBankSizeKey);
1151 	require_action(prop, exit, DEBUG_ERROR("Unable to find %s property\n", kNVRAMBankSizeKey));
1152 
1153 	data = OSDynamicCast(OSData, prop.get());
1154 	require(data, exit);
1155 
1156 	_bankSize = *((uint32_t *)data->getBytesNoCopy());
1157 
1158 	prop = entry->copyProperty(kNVRAMBankCountKey);
1159 	require_action(prop, exit, DEBUG_ERROR("Unable to find %s property\n", kNVRAMBankCountKey));
1160 
1161 	data = OSDynamicCast(OSData, prop.get());
1162 	require(data, exit);
1163 
1164 	_bankCount = *((uint32_t *)data->getBytesNoCopy());
1165 
1166 	prop = entry->copyProperty(kNVRAMCurrentBankKey);
1167 	require_action(prop, exit, DEBUG_ERROR("Unable to find %s property\n", kNVRAMCurrentBankKey));
1168 
1169 	data = OSDynamicCast(OSData, prop.get());
1170 	require(data, exit);
1171 
1172 	_currentBank = *((uint32_t *)data->getBytesNoCopy());
1173 
1174 	ok = true;
1175 
1176 	DEBUG_ALWAYS("_bankSize=%#X, _bankCount=%#X, _currentBank=%#X\n", _bankSize, _bankCount, _currentBank);
1177 
1178 exit:
1179 	return ok;
1180 }
1181 
1182 #include "IONVRAMCHRPHandler.cpp"
1183 
1184 #include "IONVRAMV3Handler.cpp"
1185 
1186 // **************************** IODTNVRAM *********************************
1187 
1188 bool
1189 IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
1190 {
1191 	OSSharedPtr<OSDictionary> dict;
1192 
1193 	DEBUG_INFO("...\n");
1194 
1195 	require(super::init(old, plane), fail);
1196 
1197 #if XNU_TARGET_OS_OSX
1198 #if CONFIG_CSR
1199 	gInternalBuild = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0);
1200 	DEBUG_INFO("gInternalBuild = %d\n", gInternalBuild);
1201 #endif // CONFIG_CSR
1202 #endif // XNU_TARGET_OS_OSX
1203 
1204 	_variableLock = IORWLockAlloc();
1205 	require(_variableLock != nullptr, fail);
1206 
1207 	_controllerLock = IOLockAlloc();
1208 	require(_controllerLock != nullptr, fail);
1209 
1210 	// Clear the IORegistryEntry property table
1211 	dict =  OSDictionary::withCapacity(1);
1212 	require(dict != nullptr, fail);
1213 
1214 	setPropertyTable(dict.get());
1215 	dict.reset();
1216 
1217 	return true;
1218 
1219 fail:
1220 	return false;
1221 }
1222 
1223 bool
1224 IODTNVRAM::start(IOService *provider)
1225 {
1226 	OSSharedPtr<OSNumber> version;
1227 
1228 	DEBUG_INFO("...\n");
1229 
1230 	require(super::start(provider), fail);
1231 
1232 	// Check if our overridden init function was called
1233 	// If not, skip any additional initialization being done here.
1234 	// This is not an error we just need to successfully exit this function to allow
1235 	// AppleEFIRuntime to proceed and take over operation
1236 	require_action(_controllerLock != nullptr, no_common, DEBUG_INFO("x86 init\n"));
1237 
1238 	_diags = new IODTNVRAMDiags;
1239 	if (!_diags || !_diags->init()) {
1240 		DEBUG_ERROR("Unable to create/init the diags service\n");
1241 		OSSafeReleaseNULL(_diags);
1242 		goto fail;
1243 	}
1244 
1245 	if (!_diags->attach(this)) {
1246 		DEBUG_ERROR("Unable to attach the diags service!\n");
1247 		OSSafeReleaseNULL(_diags);
1248 		goto fail;
1249 	}
1250 
1251 	if (!_diags->start(this)) {
1252 		DEBUG_ERROR("Unable to start the diags service!\n");
1253 		_diags->detach(this);
1254 		OSSafeReleaseNULL(_diags);
1255 		goto fail;
1256 	}
1257 
1258 	_notifier = new IODTNVRAMPlatformNotifier;
1259 	if (!_notifier || !_notifier->init()) {
1260 		DEBUG_ERROR("Unable to create/init the notifier service\n");
1261 		OSSafeReleaseNULL(_notifier);
1262 		goto fail;
1263 	}
1264 
1265 	if (!_notifier->attach(this)) {
1266 		DEBUG_ERROR("Unable to attach the notifier service!\n");
1267 		OSSafeReleaseNULL(_notifier);
1268 		goto fail;
1269 	}
1270 
1271 	if (!_notifier->start(this)) {
1272 		DEBUG_ERROR("Unable to start the notifier service!\n");
1273 		_notifier->detach(this);
1274 		OSSafeReleaseNULL(_notifier);
1275 		goto fail;
1276 	}
1277 
1278 	// This will load the proxied variable data which will call back into
1279 	// IODTNVRAM for the variable sets which will also update the system/common services
1280 	initImageFormat();
1281 
1282 	version = OSNumber::withNumber(_format->getVersion(), 32);
1283 	_diags->setProperty(kCurrentNVRAMVersionKey, version.get());
1284 
1285 	if (_format->getSystemUsed()) {
1286 		_systemService = new IODTNVRAMVariables;
1287 
1288 		if (!_systemService || !_systemService->init(gAppleSystemVariableGuid, _format->getSystemPartitionActive())) {
1289 			DEBUG_ERROR("Unable to start the system service!\n");
1290 			OSSafeReleaseNULL(_systemService);
1291 			goto no_system;
1292 		}
1293 
1294 		_systemService->setName("options-system");
1295 
1296 		if (!_systemService->attach(this)) {
1297 			DEBUG_ERROR("Unable to attach the system service!\n");
1298 			OSSafeReleaseNULL(_systemService);
1299 			goto no_system;
1300 		}
1301 
1302 		if (!_systemService->start(this)) {
1303 			DEBUG_ERROR("Unable to start the system service!\n");
1304 			_systemService->detach(this);
1305 			OSSafeReleaseNULL(_systemService);
1306 			goto no_system;
1307 		}
1308 	}
1309 
1310 no_system:
1311 	_commonService = new IODTNVRAMVariables;
1312 
1313 	if (!_commonService || !_commonService->init(gAppleNVRAMGuid, _format->getSystemPartitionActive())) {
1314 		DEBUG_ERROR("Unable to start the common service!\n");
1315 		OSSafeReleaseNULL(_commonService);
1316 		goto no_common;
1317 	}
1318 
1319 	_commonService->setName("options-common");
1320 
1321 	if (!_commonService->attach(this)) {
1322 		DEBUG_ERROR("Unable to attach the common service!\n");
1323 		OSSafeReleaseNULL(_commonService);
1324 		goto no_common;
1325 	}
1326 
1327 	if (!_commonService->start(this)) {
1328 		DEBUG_ERROR("Unable to start the common service!\n");
1329 		_commonService->detach(this);
1330 		OSSafeReleaseNULL(_commonService);
1331 		goto no_common;
1332 	}
1333 
1334 no_common:
1335 	return true;
1336 
1337 fail:
1338 	stop(provider);
1339 	return false;
1340 }
1341 
1342 void
1343 IODTNVRAM::initImageFormat(void)
1344 {
1345 	OSSharedPtr<IORegistryEntry> entry;
1346 	OSSharedPtr<OSObject>        prop;
1347 	const char                   *proxyDataKey = "nvram-proxy-data";
1348 	const char                   *bankSizeKey = "nvram-bank-size";
1349 	OSData                       *data = nullptr;
1350 	uint32_t                     size = 0;
1351 	const uint8_t                *image = nullptr;
1352 
1353 	entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1354 
1355 	require(entry != nullptr, skip);
1356 
1357 	prop = entry->copyProperty(bankSizeKey);
1358 	require(prop != nullptr, skip);
1359 
1360 	data = OSDynamicCast(OSData, prop.get());
1361 	require(data != nullptr, skip);
1362 
1363 	size = *((uint32_t*)data->getBytesNoCopy());
1364 	DEBUG_ALWAYS("NVRAM size is %u bytes\n", size);
1365 
1366 	prop = entry->copyProperty(proxyDataKey);
1367 	require(prop != nullptr, skip);
1368 
1369 	data = OSDynamicCast(OSData, prop.get());
1370 	require_action(data != nullptr, skip, DEBUG_ERROR("No proxy data!\n"));
1371 
1372 	image = (const uint8_t *)data->getBytesNoCopy();
1373 
1374 skip:
1375 	if (IONVRAMV3Handler::isValidImage(image, size)) {
1376 		_format = IONVRAMV3Handler::init(this, image, size, _varDict);
1377 		require_action(_format, skip, panic("IONVRAMV3Handler creation failed\n"));
1378 	} else {
1379 		_format = IONVRAMCHRPHandler::init(this, image, size, _varDict);
1380 		require_action(_format, skip, panic("IONVRAMCHRPHandler creation failed\n"));
1381 	}
1382 
1383 	_format->unserializeVariables();
1384 
1385 	dumpDict(_varDict.get());
1386 
1387 #if defined(RELEASE)
1388 	if (entry != nullptr) {
1389 		entry->removeProperty(proxyDataKey);
1390 	}
1391 #endif
1392 
1393 	_lastDeviceSync = 0;
1394 	_freshInterval = true;
1395 }
1396 
1397 void
1398 IODTNVRAM::registerNVRAMController(IONVRAMController *controller)
1399 {
1400 	DEBUG_INFO("setting controller\n");
1401 
1402 	NVRAMWRITELOCK();
1403 	CONTROLLERLOCK();
1404 
1405 	_format->setController(controller);
1406 
1407 	CONTROLLERUNLOCK();
1408 	NVRAMUNLOCK();
1409 
1410 	return;
1411 }
1412 
1413 bool
1414 IODTNVRAM::safeToSync(void)
1415 {
1416 	AbsoluteTime delta;
1417 	UInt64       delta_ns;
1418 	SInt32       delta_secs;
1419 
1420 	// delta interval went by
1421 	clock_get_uptime(&delta);
1422 
1423 	// Figure it in seconds.
1424 	absolutetime_to_nanoseconds(delta, &delta_ns);
1425 	delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
1426 
1427 	if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
1428 		_lastDeviceSync = delta_secs;
1429 		_freshInterval = false;
1430 		return true;
1431 	}
1432 
1433 	return false;
1434 }
1435 
1436 void
1437 IODTNVRAM::syncInternal(bool rateLimit)
1438 {
1439 	DEBUG_INFO("rateLimit=%d\n", rateLimit);
1440 
1441 	if (!SAFE_TO_LOCK()) {
1442 		DEBUG_INFO("cannot lock\n");
1443 		return;
1444 	}
1445 
1446 	// Rate limit requests to sync. Drivers that need this rate limiting will
1447 	// shadow the data and only write to flash when they get a sync call
1448 	if (rateLimit) {
1449 		if (safeToSync() == false) {
1450 			DEBUG_INFO("safeToSync()=false\n");
1451 			return;
1452 		}
1453 	}
1454 
1455 	DEBUG_INFO("Calling sync()\n");
1456 	record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "sync", "triggered");
1457 
1458 	NVRAMREADLOCK();
1459 	CONTROLLERLOCK();
1460 
1461 	_format->sync();
1462 
1463 	CONTROLLERUNLOCK();
1464 	NVRAMUNLOCK();
1465 
1466 	if (_diags) {
1467 		OSSharedPtr<OSNumber> generation = OSNumber::withNumber(_format->getGeneration(), 32);
1468 		_diags->setProperty(kCurrentGenerationCountKey, generation.get());
1469 	}
1470 }
1471 
1472 void
1473 IODTNVRAM::sync(void)
1474 {
1475 	syncInternal(false);
1476 }
1477 
1478 void
1479 IODTNVRAM::reload(void)
1480 {
1481 	_format->reload();
1482 }
1483 
1484 bool
1485 IODTNVRAM::serializeProperties(OSSerialize *s) const
1486 {
1487 	const OSSymbol                    *canonicalKey;
1488 	OSSharedPtr<OSDictionary>         localVarDict, returnDict;
1489 	OSSharedPtr<OSCollectionIterator> iter;
1490 	bool                              ok = false;
1491 	unsigned int                      totalCapacity = 0;
1492 	uuid_t                            varGuid;
1493 	const char *                      varName;
1494 
1495 	NVRAMREADLOCK();
1496 	if (_varDict) {
1497 		localVarDict = OSDictionary::withDictionary(_varDict.get());
1498 	}
1499 	NVRAMUNLOCK();
1500 
1501 	if (localVarDict != nullptr) {
1502 		totalCapacity =  localVarDict->getCapacity();
1503 	}
1504 
1505 	returnDict = OSDictionary::withCapacity(totalCapacity);
1506 
1507 	if (returnDict == nullptr) {
1508 		DEBUG_ERROR("No dictionary\n");
1509 		goto exit;
1510 	}
1511 
1512 	// Copy system entries first if present then copy unique other entries
1513 	iter = OSCollectionIterator::withCollection(localVarDict.get());
1514 	if (iter == nullptr) {
1515 		DEBUG_ERROR("failed to create iterator\n");
1516 		goto exit;
1517 	}
1518 
1519 	while ((canonicalKey = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1520 		parseVariableName(canonicalKey, &varGuid, &varName);
1521 
1522 		if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) &&
1523 		    verifyPermission(kIONVRAMOperationRead, varGuid, varName, _format->getSystemPartitionActive())) {
1524 			OSSharedPtr<const OSSymbol> returnKey = OSSymbol::withCString(varName);
1525 			returnDict->setObject(returnKey.get(), localVarDict->getObject(canonicalKey));
1526 		}
1527 	}
1528 
1529 	iter.reset();
1530 
1531 	iter = OSCollectionIterator::withCollection(localVarDict.get());
1532 	if (iter == nullptr) {
1533 		DEBUG_ERROR("failed to create iterator\n");
1534 		goto exit;
1535 	}
1536 
1537 	while ((canonicalKey = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1538 		parseVariableName(canonicalKey, &varGuid, &varName);
1539 
1540 		if (uuid_compare(varGuid, gAppleNVRAMGuid) == 0) {
1541 			if (returnDict->getObject(varName) != nullptr) {
1542 				// Skip non uniques
1543 				continue;
1544 			}
1545 
1546 			if (verifyPermission(kIONVRAMOperationRead, varGuid, varName, _format->getSystemPartitionActive())) {
1547 				OSSharedPtr<const OSSymbol> returnKey = OSSymbol::withCString(varName);
1548 				returnDict->setObject(returnKey.get(), localVarDict->getObject(canonicalKey));
1549 			}
1550 		}
1551 	}
1552 
1553 	ok = returnDict->serialize(s);
1554 
1555 exit:
1556 	DEBUG_INFO("ok=%d\n", ok);
1557 
1558 	return ok;
1559 }
1560 
1561 IOReturn
1562 IODTNVRAM::flushGUID(const uuid_t guid, IONVRAMOperation op)
1563 {
1564 	IOReturn ret = kIOReturnSuccess;
1565 
1566 	if (_format->getSystemPartitionActive() && (uuid_compare(guid, gAppleSystemVariableGuid) == 0)) {
1567 		ret = _format->flush(guid, op);
1568 
1569 		DEBUG_INFO("system variables flushed, ret=%08x\n", ret);
1570 	} else if (uuid_compare(guid, gAppleNVRAMGuid) == 0) {
1571 		ret = _format->flush(guid, op);
1572 
1573 		DEBUG_INFO("common variables flushed, ret=%08x\n", ret);
1574 	}
1575 
1576 	return ret;
1577 }
1578 
1579 bool
1580 IODTNVRAM::handleSpecialVariables(const char *name, const uuid_t guid, const OSObject *obj, IOReturn *error)
1581 {
1582 	IOReturn ret = kIOReturnSuccess;
1583 	bool special = false;
1584 
1585 	NVRAMLOCKASSERTEXCLUSIVE();
1586 
1587 	// ResetNVRam flushes both regions in one call
1588 	// Obliterate can flush either separately
1589 	if (strcmp(name, "ObliterateNVRam") == 0) {
1590 		special = true;
1591 		ret = flushGUID(guid, kIONVRAMOperationObliterate);
1592 	} else if (strcmp(name, "ResetNVRam") == 0) {
1593 		special = true;
1594 		ret = flushGUID(gAppleSystemVariableGuid, kIONVRAMOperationReset);
1595 
1596 		if (ret != kIOReturnSuccess) {
1597 			goto exit;
1598 		}
1599 
1600 		ret = flushGUID(gAppleNVRAMGuid, kIONVRAMOperationReset);
1601 	}
1602 
1603 exit:
1604 	if (error) {
1605 		*error = ret;
1606 	}
1607 
1608 	return special;
1609 }
1610 
1611 OSSharedPtr<OSObject>
1612 IODTNVRAM::copyPropertyWithGUIDAndName(const uuid_t guid, const char *name) const
1613 {
1614 	OSSharedPtr<const OSSymbol> canonicalKey;
1615 	OSSharedPtr<OSObject>       theObject;
1616 	uuid_t                      newGuid;
1617 
1618 	if (_varDict == nullptr) {
1619 		DEBUG_INFO("No dictionary\n");
1620 		goto exit;
1621 	}
1622 
1623 	if (!verifyPermission(kIONVRAMOperationRead, guid, name, _format->getSystemPartitionActive())) {
1624 		DEBUG_INFO("Not privileged\n");
1625 		goto exit;
1626 	}
1627 
1628 	translateGUID(guid, name, newGuid, _format->getSystemPartitionActive());
1629 
1630 	canonicalKey = keyWithGuidAndCString(newGuid, name);
1631 
1632 	NVRAMREADLOCK();
1633 	theObject.reset(_varDict->getObject(canonicalKey.get()), OSRetain);
1634 	NVRAMUNLOCK();
1635 
1636 	if (_diags) {
1637 		_diags->logVariable(getPartitionTypeForGUID(newGuid), kIONVRAMOperationRead, name, NULL);
1638 	}
1639 
1640 	if (theObject != nullptr) {
1641 		DEBUG_INFO("%s has object\n", canonicalKey.get()->getCStringNoCopy());
1642 	} else {
1643 		DEBUG_INFO("%s no entry\n", canonicalKey.get()->getCStringNoCopy());
1644 	}
1645 
1646 exit:
1647 	return theObject;
1648 }
1649 
1650 OSSharedPtr<OSObject>
1651 IODTNVRAM::copyProperty(const OSSymbol *aKey) const
1652 {
1653 	const char            *variableName;
1654 	uuid_t                varGuid;
1655 
1656 	if (skipKey(aKey)) {
1657 		return nullptr;
1658 	}
1659 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1660 
1661 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1662 
1663 	return copyPropertyWithGUIDAndName(varGuid, variableName);
1664 }
1665 
1666 OSSharedPtr<OSObject>
1667 IODTNVRAM::copyProperty(const char *aKey) const
1668 {
1669 	OSSharedPtr<const OSSymbol> keySymbol;
1670 	OSSharedPtr<OSObject>       theObject;
1671 
1672 	keySymbol = OSSymbol::withCString(aKey);
1673 	if (keySymbol != nullptr) {
1674 		theObject = copyProperty(keySymbol.get());
1675 	}
1676 
1677 	return theObject;
1678 }
1679 
1680 OSObject *
1681 IODTNVRAM::getProperty(const OSSymbol *aKey) const
1682 {
1683 	// The shared pointer gets released at the end of the function,
1684 	// and returns a view into theObject.
1685 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1686 
1687 	return theObject.get();
1688 }
1689 
1690 OSObject *
1691 IODTNVRAM::getProperty(const char *aKey) const
1692 {
1693 	// The shared pointer gets released at the end of the function,
1694 	// and returns a view into theObject.
1695 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1696 
1697 	return theObject.get();
1698 }
1699 
1700 IOReturn
1701 IODTNVRAM::setPropertyWithGUIDAndName(const uuid_t guid, const char *name, OSObject *anObject)
1702 {
1703 	IOReturn              ret = kIOReturnSuccess;
1704 	bool                  remove = false;
1705 	OSString              *tmpString = nullptr;
1706 	OSSharedPtr<OSObject> propObject;
1707 	OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
1708 	bool                  deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
1709 	bool                  ok;
1710 	size_t                propDataSize = 0;
1711 	uuid_t                newGuid;
1712 
1713 	deletePropertyKey = strncmp(name, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
1714 	syncNowPropertyKey = strncmp(name, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
1715 	forceSyncNowPropertyKey = strncmp(name, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
1716 
1717 	if (deletePropertyKey) {
1718 		tmpString = OSDynamicCast(OSString, anObject);
1719 		if (tmpString != nullptr) {
1720 			const char *variableName;
1721 			uuid_t     valueVarGuid;
1722 			bool       guidProvided;
1723 			IOReturn   removeRet;
1724 
1725 			guidProvided = parseVariableName(tmpString->getCStringNoCopy(), &valueVarGuid, &variableName);
1726 
1727 			// nvram tool will provide a "nvram -d var" or "nvram -d guid:var" as
1728 			// kIONVRAMDeletePropertyKey=var or kIONVRAMDeletePropertyKey=guid:var
1729 			// that will come into this function as (gAppleNVRAMGuid, varname, nullptr)
1730 			// if we provide the "-z" flag to the nvram tool this function will come in as
1731 			// (gAppleSystemVariableGuid, varname, nullptr). We are reparsing the value string,
1732 			// if there is a GUID provided with the value then use that GUID otherwise use the
1733 			// guid that was provided via the node selection or default.
1734 			if (guidProvided == false) {
1735 				DEBUG_INFO("Removing with API provided GUID\n");
1736 				removeRet = removePropertyWithGUIDAndName(guid, variableName);
1737 			} else {
1738 				DEBUG_INFO("Removing with value provided GUID\n");
1739 				removeRet = removePropertyWithGUIDAndName(valueVarGuid, variableName);
1740 			}
1741 
1742 			DEBUG_INFO("kIONVRAMDeletePropertyKey found, removeRet=%#08x\n", removeRet);
1743 		} else {
1744 			DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1745 			ret = kIOReturnError;
1746 		}
1747 		goto exit;
1748 	} else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
1749 		tmpString = OSDynamicCast(OSString, anObject);
1750 		DEBUG_INFO("NVRAM sync key %s found\n", name);
1751 		if (tmpString != nullptr) {
1752 			// We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1753 			syncInternal(syncNowPropertyKey);
1754 		} else {
1755 			DEBUG_INFO("%s value needs to be an OSString\n", name);
1756 			ret = kIOReturnError;
1757 		}
1758 		goto exit;
1759 	}
1760 
1761 	if (!verifyPermission(kIONVRAMOperationWrite, guid, name, _format->getSystemPartitionActive())) {
1762 		DEBUG_INFO("Not privileged\n");
1763 		ret = kIOReturnNotPrivileged;
1764 		goto exit;
1765 	}
1766 
1767 	// Make sure the object is of the correct type.
1768 	switch (getVariableType(name)) {
1769 	case kOFVariableTypeBoolean:
1770 		propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
1771 		if (propObject) {
1772 			record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s to %d", name, ((OSBoolean *)propObject.get())->getValue());
1773 		}
1774 		break;
1775 
1776 	case kOFVariableTypeNumber:
1777 		propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
1778 		if (propObject) {
1779 			record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s to %#llx", name, ((OSNumber *)propObject.get())->unsigned64BitValue());
1780 		}
1781 		break;
1782 
1783 	case kOFVariableTypeString:
1784 		propObject = OSDynamicPtrCast<OSString>(sharedObject);
1785 		if (propObject != nullptr) {
1786 			record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s to %s", name, ((OSString *)propObject.get())->getCStringNoCopy());
1787 
1788 			propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
1789 
1790 			if ((strncmp(name, kIONVRAMBootArgsKey, sizeof(kIONVRAMBootArgsKey)) == 0) && (propDataSize >= BOOT_LINE_LENGTH)) {
1791 				DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
1792 				ret = kIOReturnNoSpace;
1793 				goto exit;
1794 			}
1795 		}
1796 		break;
1797 
1798 	case kOFVariableTypeData:
1799 		propObject = OSDynamicPtrCast<OSData>(sharedObject);
1800 		if (propObject == nullptr) {
1801 			tmpString = OSDynamicCast(OSString, sharedObject.get());
1802 			if (tmpString != nullptr) {
1803 				propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
1804 				    tmpString->getLength());
1805 			}
1806 		}
1807 
1808 		if (propObject != nullptr) {
1809 			propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
1810 			record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "write", "%s with data size %#x", name, ((OSData *)propObject.get())->getLength());
1811 		}
1812 
1813 #if defined(XNU_TARGET_OS_OSX)
1814 		if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
1815 			remove = true;
1816 		}
1817 #endif /* defined(XNU_TARGET_OS_OSX) */
1818 		break;
1819 	default:
1820 		break;
1821 	}
1822 
1823 	if (propObject == nullptr) {
1824 		DEBUG_INFO("No property object\n");
1825 		ret = kIOReturnBadArgument;
1826 		goto exit;
1827 	}
1828 
1829 	if (!verifyWriteSizeLimit(guid, name, propDataSize)) {
1830 		DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, name);
1831 		ret = kIOReturnNoSpace;
1832 		goto exit;
1833 	}
1834 
1835 	NVRAMWRITELOCK();
1836 	ok = handleSpecialVariables(name, guid, propObject.get(), &ret);
1837 	NVRAMUNLOCK();
1838 
1839 	if (ok) {
1840 		goto exit;
1841 	}
1842 
1843 	if (remove == false) {
1844 		DEBUG_INFO("Adding object\n");
1845 
1846 		translateGUID(guid, name, newGuid, _format->getSystemPartitionActive());
1847 
1848 		NVRAMWRITELOCK();
1849 
1850 		ret = _format->setVariable(newGuid, name, propObject.get());
1851 
1852 		NVRAMUNLOCK();
1853 	} else {
1854 		DEBUG_INFO("Removing object\n");
1855 		ret = removePropertyWithGUIDAndName(guid, name);
1856 	}
1857 
1858 	if (tmpString) {
1859 		propObject.reset();
1860 	}
1861 
1862 exit:
1863 	DEBUG_INFO("ret=%#08x\n", ret);
1864 
1865 	return ret;
1866 }
1867 
1868 IOReturn
1869 IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
1870 {
1871 	const char *variableName;
1872 	uuid_t     varGuid;
1873 
1874 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1875 
1876 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1877 
1878 	return setPropertyWithGUIDAndName(varGuid, variableName, anObject);
1879 }
1880 
1881 bool
1882 IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
1883 {
1884 	return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
1885 }
1886 
1887 void
1888 IODTNVRAM::removeProperty(const OSSymbol *aKey)
1889 {
1890 	IOReturn ret;
1891 
1892 	ret = removePropertyInternal(aKey);
1893 
1894 	if (ret != kIOReturnSuccess) {
1895 		DEBUG_INFO("removePropertyInternal failed, ret=%#08x\n", ret);
1896 	}
1897 }
1898 
1899 IOReturn
1900 IODTNVRAM::removePropertyWithGUIDAndName(const uuid_t guid, const char *name)
1901 {
1902 	IOReturn ret;
1903 	uuid_t   newGuid;
1904 
1905 	DEBUG_INFO("name=%s\n", name);
1906 
1907 	if (_varDict == nullptr) {
1908 		DEBUG_INFO("No dictionary\n");
1909 		ret = kIOReturnNotFound;
1910 		goto exit;
1911 	}
1912 
1913 	if (!verifyPermission(kIONVRAMOperationDelete, guid, name, _format->getSystemPartitionActive())) {
1914 		DEBUG_INFO("Not privileged\n");
1915 		ret = kIOReturnNotPrivileged;
1916 		goto exit;
1917 	}
1918 
1919 	translateGUID(guid, name, newGuid, _format->getSystemPartitionActive());
1920 
1921 	NVRAMWRITELOCK();
1922 
1923 	ret = _format->setVariable(newGuid, name, nullptr);
1924 
1925 	if (ret != kIOReturnSuccess) {
1926 		DEBUG_INFO("%s not found\n", name);
1927 		ret = kIOReturnNotFound;
1928 	}
1929 
1930 	NVRAMUNLOCK();
1931 
1932 	record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_NVRAM, "delete", "%s", name);
1933 
1934 exit:
1935 	return ret;
1936 }
1937 
1938 IOReturn
1939 IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
1940 {
1941 	IOReturn   ret;
1942 	const char *variableName;
1943 	uuid_t     varGuid;
1944 
1945 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1946 
1947 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1948 
1949 	ret = removePropertyWithGUIDAndName(varGuid, variableName);
1950 
1951 	return ret;
1952 }
1953 
1954 IOReturn
1955 IODTNVRAM::setProperties(OSObject *properties)
1956 {
1957 	IOReturn                          ret = kIOReturnSuccess;
1958 	OSObject                          *object;
1959 	const OSSymbol                    *key;
1960 	OSDictionary                      *dict;
1961 	OSSharedPtr<OSCollectionIterator> iter;
1962 
1963 	dict = OSDynamicCast(OSDictionary, properties);
1964 	if (dict == nullptr) {
1965 		DEBUG_ERROR("Not a dictionary\n");
1966 		return kIOReturnBadArgument;
1967 	}
1968 
1969 	iter = OSCollectionIterator::withCollection(dict);
1970 	if (iter == nullptr) {
1971 		DEBUG_ERROR("Couldn't create iterator\n");
1972 		return kIOReturnBadArgument;
1973 	}
1974 
1975 	while (ret == kIOReturnSuccess) {
1976 		key = OSDynamicCast(OSSymbol, iter->getNextObject());
1977 		if (key == nullptr) {
1978 			break;
1979 		}
1980 
1981 		object = dict->getObject(key);
1982 		if (object == nullptr) {
1983 			continue;
1984 		}
1985 
1986 		ret = setPropertyInternal(key, object);
1987 	}
1988 
1989 	DEBUG_INFO("ret=%#08x\n", ret);
1990 
1991 	return ret;
1992 }
1993 
1994 // ********************** Deprecated ********************
1995 
1996 IOReturn
1997 IODTNVRAM::readXPRAM(IOByteCount offset, uint8_t *buffer,
1998     IOByteCount length)
1999 {
2000 	return kIOReturnUnsupported;
2001 }
2002 
2003 IOReturn
2004 IODTNVRAM::writeXPRAM(IOByteCount offset, uint8_t *buffer,
2005     IOByteCount length)
2006 {
2007 	return kIOReturnUnsupported;
2008 }
2009 
2010 IOReturn
2011 IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
2012     const OSSymbol **name,
2013     OSData **value)
2014 {
2015 	return kIOReturnUnsupported;
2016 }
2017 
2018 IOReturn
2019 IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
2020     const OSSymbol *name,
2021     OSData *value)
2022 {
2023 	return kIOReturnUnsupported;
2024 }
2025 
2026 OSDictionary *
2027 IODTNVRAM::getNVRAMPartitions(void)
2028 {
2029 	return NULL;
2030 }
2031 
2032 IOReturn
2033 IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
2034     IOByteCount offset, uint8_t *buffer,
2035     IOByteCount length)
2036 {
2037 	return kIOReturnUnsupported;
2038 }
2039 
2040 IOReturn
2041 IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
2042     IOByteCount offset, uint8_t *buffer,
2043     IOByteCount length)
2044 {
2045 	return kIOReturnUnsupported;
2046 }
2047 
2048 IOByteCount
2049 IODTNVRAM::savePanicInfo(uint8_t *buffer, IOByteCount length)
2050 {
2051 	return 0;
2052 }
2053