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