xref: /xnu-8020.121.3/iokit/Kernel/IONVRAM.cpp (revision fdd8201d7b966f0c3ea610489d29bd841d358941)
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, gAppleSystemVariableGuid) == 0) {
291 		return kIONVRAMPartitionSystem;
292 	} else {
293 		return kIONVRAMPartitionCommon;
294 	}
295 }
296 
297 static IONVRAMVariableType
getVariableType(const char * propName)298 getVariableType(const char *propName)
299 {
300 	const VariableTypeEntry *entry;
301 
302 	entry = gVariableTypes;
303 	while (entry->name != nullptr) {
304 		if (strcmp(entry->name, propName) == 0) {
305 			break;
306 		}
307 		entry++;
308 	}
309 
310 	return entry->type;
311 }
312 
313 static IONVRAMVariableType
getVariableType(const OSSymbol * propSymbol)314 getVariableType(const OSSymbol *propSymbol)
315 {
316 	return getVariableType(propSymbol->getCStringNoCopy());
317 }
318 
319 static VariablePermission
getVariablePermission(const char * propName)320 getVariablePermission(const char *propName)
321 {
322 	const VariablePermissionEntry *entry;
323 
324 	entry = gVariablePermissions;
325 	while (entry->name != nullptr) {
326 		if (strcmp(entry->name, propName) == 0) {
327 			break;
328 		}
329 		entry++;
330 	}
331 
332 	return entry->p;
333 }
334 
335 static bool
variableInAllowList(const char * varName)336 variableInAllowList(const char *varName)
337 {
338 	unsigned int i = 0;
339 
340 	while (gNVRAMSystemList[i] != nullptr) {
341 		if (strcmp(varName, gNVRAMSystemList[i]) == 0) {
342 			return true;
343 		}
344 		i++;
345 	}
346 
347 	return false;
348 }
349 
350 static bool
verifyWriteSizeLimit(const uuid_t varGuid,const char * variableName,size_t propDataSize)351 verifyWriteSizeLimit(const uuid_t varGuid, const char *variableName, size_t propDataSize)
352 {
353 	if (variableInAllowList(variableName)) {
354 		if (strnstr(variableName, "breadcrumbs", strlen(variableName)) != NULL) {
355 			return propDataSize <= 1024;
356 		} else {
357 			return propDataSize <= 768;
358 		}
359 	}
360 
361 	return true;
362 }
363 
364 #if defined(DEBUG) || defined(DEVELOPMENT)
365 static const char *
getNVRAMOpString(IONVRAMOperation op)366 getNVRAMOpString(IONVRAMOperation op)
367 {
368 	switch (op) {
369 	case kIONVRAMOperationRead:
370 		return "Read";
371 	case kIONVRAMOperationWrite:
372 		return "Write";
373 	case kIONVRAMOperationDelete:
374 		return "Delete";
375 	case kIONVRAMOperationObliterate:
376 		return "Obliterate";
377 	case kIONVRAMOperationReset:
378 		return "Reset";
379 	case kIONVRAMOperationInit:
380 		return "Init";
381 	default:
382 		return "Unknown";
383 	}
384 }
385 #endif
386 
387 static bool
verifyPermission(IONVRAMOperation op,const uuid_t varGuid,const char * varName)388 verifyPermission(IONVRAMOperation op, const uuid_t varGuid, const char *varName)
389 {
390 	VariablePermission perm;
391 	bool kernel, writeEntitled = false, readEntitled = false, allowList, systemGuid = false, systemEntitled = false, systemInternalEntitled = false, systemAllow, systemReadHiddenAllow = false;
392 	bool admin = false;
393 	bool ok = false;
394 
395 	perm = getVariablePermission(varName);
396 
397 	kernel = current_task() == kernel_task;
398 
399 	if (perm.Bits.KernelOnly) {
400 		DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
401 		ok = kernel;
402 		goto exit;
403 	}
404 
405 	allowList              = variableInAllowList(varName);
406 	systemGuid             = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
407 	admin                  = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess;
408 	writeEntitled          = IOCurrentTaskHasEntitlement(kIONVRAMWriteAccessKey);
409 	readEntitled           = IOCurrentTaskHasEntitlement(kIONVRAMReadAccessKey);
410 	systemEntitled         = IOCurrentTaskHasEntitlement(kIONVRAMSystemAllowKey);
411 	systemInternalEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemInternalAllowKey);
412 	systemReadHiddenAllow  = IOCurrentTaskHasEntitlement(kIONVRAMSystemHiddenAllowKey);
413 
414 	systemAllow = systemEntitled || (systemInternalEntitled && gInternalBuild) || kernel;
415 
416 	switch (op) {
417 	case kIONVRAMOperationRead:
418 		if (systemGuid && perm.Bits.SystemReadHidden) {
419 			ok = systemReadHiddenAllow;
420 		} else if (kernel || admin || readEntitled || perm.Bits.FullAccess) {
421 			ok = true;
422 		}
423 		break;
424 
425 	case kIONVRAMOperationWrite:
426 		if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
427 			if (systemGuid) {
428 				if (allowList) {
429 					if (!systemAllow) {
430 						DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
431 					}
432 				} else if (!systemAllow) {
433 					DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
434 					break;
435 				}
436 			}
437 			ok = true;
438 		}
439 		break;
440 
441 	case kIONVRAMOperationDelete:
442 	case kIONVRAMOperationObliterate:
443 	case kIONVRAMOperationReset:
444 		if (perm.Bits.NeverAllowedToDelete) {
445 			DEBUG_INFO("Never allowed to delete %s\n", varName);
446 			break;
447 		} else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) {
448 			DEBUG_INFO("Not allowed to obliterate %s\n", varName);
449 			break;
450 		} else if ((op == kIONVRAMOperationDelete) && perm.Bits.ResetNVRAMOnlyDelete) {
451 			DEBUG_INFO("Only allowed to delete %s via NVRAM reset\n", varName);
452 			break;
453 		}
454 
455 		if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
456 			if (systemGuid) {
457 				if (allowList) {
458 					if (!systemAllow) {
459 						DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
460 					}
461 				} else if (!systemAllow) {
462 					DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
463 					break;
464 				}
465 			}
466 			ok = true;
467 		}
468 		break;
469 
470 	case kIONVRAMOperationInit:
471 		break;
472 	}
473 
474 exit:
475 	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",
476 	    kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled, systemInternalEntitled, systemReadHiddenAllow, perm.Bits.UserWrite);
477 
478 	return ok;
479 }
480 
481 static bool
verifyPermission(IONVRAMOperation op,const uuid_t varGuid,const OSSymbol * varName)482 verifyPermission(IONVRAMOperation op, const uuid_t varGuid, const OSSymbol *varName)
483 {
484 	return verifyPermission(op, varGuid, varName->getCStringNoCopy());
485 }
486 
487 /*
488  * Parse a variable name of the form "GUID:name".
489  * If the name cannot be parsed, substitute the Apple global variable GUID.
490  * Returns TRUE if a GUID was found in the name, FALSE otherwise.
491  * The guidResult and nameResult arguments may be nullptr if you just want
492  * to check the format of the string.
493  */
494 static bool
parseVariableName(const char * key,uuid_t * guidResult,const char ** nameResult)495 parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult)
496 {
497 	uuid_string_t temp    = {0};
498 	size_t        keyLen  = strlen(key);
499 	bool          ok      = false;
500 	const char    *name   = key;
501 	uuid_t        guid;
502 
503 	if (keyLen > sizeof(temp)) {
504 		// check for at least UUID + ":" + more
505 		memcpy(temp, key, sizeof(temp) - 1);
506 
507 		if ((uuid_parse(temp, guid) == 0) &&
508 		    (key[sizeof(temp) - 1] == ':')) {
509 			name = key + sizeof(temp);
510 			ok     = true;
511 		}
512 	}
513 
514 	if (guidResult) {
515 		ok ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
516 	}
517 	if (nameResult) {
518 		*nameResult = name;
519 	}
520 
521 	return ok;
522 }
523 
524 static bool
skipKey(const OSSymbol * aKey)525 skipKey(const OSSymbol *aKey)
526 {
527 	return aKey->isEqualTo(kIOClassNameOverrideKey) ||
528 	       aKey->isEqualTo(kIOBSDNameKey) ||
529 	       aKey->isEqualTo(kIOBSDNamesKey) ||
530 	       aKey->isEqualTo(kIOBSDMajorKey) ||
531 	       aKey->isEqualTo(kIOBSDMinorKey) ||
532 	       aKey->isEqualTo(kIOBSDUnitKey) ||
533 	       aKey->isEqualTo(kIOUserServicePropertiesKey) ||
534 	       aKey->isEqualTo(kIOMatchCategoryKey);
535 }
536 
537 
538 // ************************** IODTNVRAMDiags ****************************
539 
540 #define kIODTNVRAMDiagsStatsKey   "Stats"
541 #define kIODTNVRAMDiagsInitKey    "Init"
542 #define kIODTNVRAMDiagsReadKey    "Read"
543 #define kIODTNVRAMDiagsWriteKey   "Write"
544 #define kIODTNVRAMDiagsDeleteKey  "Delete"
545 #define kIODTNVRAMDiagsNameKey    "Name"
546 #define kIODTNVRAMDiagsSizeKey    "Size"
547 #define kIODTNVRAMDiagsPresentKey "Present"
548 
549 // private IOService based class for publishing diagnostic info for IODTNVRAM
550 class IODTNVRAMDiags : public IOService
551 {
552 	OSDeclareDefaultStructors(IODTNVRAMDiags)
553 private:
554 	IODTNVRAM                 *_provider;
555 	IORWLock                  *_variableLock;
556 	OSSharedPtr<OSDictionary> _stats;
557 
558 	bool serializeStats(void *, OSSerialize * serializer);
559 
560 public:
561 	bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
562 	void logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data);
563 };
564 
OSDefineMetaClassAndStructors(IODTNVRAMDiags,IOService)565 OSDefineMetaClassAndStructors(IODTNVRAMDiags, IOService)
566 
567 bool
568 IODTNVRAMDiags::start(IOService * provider)
569 {
570 	OSSharedPtr<OSSerializer> serializer;
571 
572 	require(super::start(provider), error);
573 
574 	_variableLock = IORWLockAlloc();
575 	require(_variableLock != nullptr, error);
576 
577 	_provider = OSDynamicCast(IODTNVRAM, provider);
578 	require(_provider != nullptr, error);
579 
580 	_stats = OSDictionary::withCapacity(1);
581 	require(_stats != nullptr, error);
582 
583 	serializer = OSSerializer::forTarget(this, OSMemberFunctionCast(OSSerializerCallback, this, &IODTNVRAMDiags::serializeStats));
584 	require(serializer != nullptr, error);
585 
586 	setProperty(kIODTNVRAMDiagsStatsKey, serializer.get());
587 
588 	registerService();
589 
590 	return true;
591 
592 error:
593 	stop(provider);
594 
595 	return false;
596 }
597 
598 void
logVariable(NVRAMPartitionType region,IONVRAMOperation op,const char * name,void * data)599 IODTNVRAMDiags::logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data)
600 {
601 	// "Stats"        : OSDictionary
602 	// - "XX:varName" : OSDictionary, XX is the region value prefix to distinguish which dictionary the variable is in
603 	//   - "Init"     : OSBoolean True/present if variable present at initialization
604 	//   - "Read"     : OSNumber count
605 	//   - "Write"    : OSNumber count
606 	//   - "Delete"   : OSNumber count
607 	//   - "Size"     : OSNumber size, latest size from either init or write
608 	//   - "Present"  : OSBoolean True/False if variable is present or not
609 	char                      *entryKey;
610 	size_t                    entryKeySize;
611 	OSSharedPtr<OSDictionary> existingEntry;
612 	OSSharedPtr<OSNumber>     currentCount;
613 	OSSharedPtr<OSNumber>     varSize;
614 	const char                *opCountKey = nullptr;
615 
616 	entryKeySize = strlen("XX:") + strlen(name) +  1;
617 	entryKey = IONewData(char, entryKeySize);
618 	require(entryKey, exit);
619 
620 	snprintf(entryKey, entryKeySize, "%02X:%s", region, name);
621 
622 	NVRAMWRITELOCK();
623 	existingEntry.reset(OSDynamicCast(OSDictionary, _stats->getObject(entryKey)), OSRetain);
624 
625 	if (existingEntry == nullptr) {
626 		existingEntry = OSDictionary::withCapacity(4);
627 	}
628 
629 	switch (op) {
630 	case kIONVRAMOperationRead:
631 		opCountKey = kIODTNVRAMDiagsReadKey;
632 		if (existingEntry->getObject(kIODTNVRAMDiagsPresentKey) == nullptr) {
633 			existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
634 		}
635 		break;
636 	case kIONVRAMOperationWrite:
637 		opCountKey = kIODTNVRAMDiagsWriteKey;
638 		varSize = OSNumber::withNumber((size_t)data, 64);
639 		existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
640 		existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
641 		break;
642 	case kIONVRAMOperationDelete:
643 	case kIONVRAMOperationObliterate:
644 	case kIONVRAMOperationReset:
645 		opCountKey = kIODTNVRAMDiagsDeleteKey;
646 		existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
647 		break;
648 	case kIONVRAMOperationInit:
649 		varSize = OSNumber::withNumber((size_t)data, 64);
650 		existingEntry->setObject(kIODTNVRAMDiagsInitKey, varSize);
651 		existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
652 		existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
653 		break;
654 	default:
655 		goto exit;
656 	}
657 
658 	if (opCountKey) {
659 		currentCount.reset(OSDynamicCast(OSNumber, existingEntry->getObject(opCountKey)), OSRetain);
660 
661 		if (currentCount == nullptr) {
662 			currentCount = OSNumber::withNumber(1, 64);
663 		} else {
664 			currentCount->addValue(1);
665 		}
666 
667 		existingEntry->setObject(opCountKey, currentCount);
668 	}
669 
670 	_stats->setObject(entryKey, existingEntry);
671 	NVRAMUNLOCK();
672 
673 exit:
674 	IODeleteData(entryKey, char, entryKeySize);
675 
676 	return;
677 }
678 
679 bool
serializeStats(void *,OSSerialize * serializer)680 IODTNVRAMDiags::serializeStats(void *, OSSerialize * serializer)
681 {
682 	bool ok;
683 
684 	NVRAMREADLOCK();
685 	ok = _stats->serialize(serializer);
686 	NVRAMUNLOCK();
687 
688 	return ok;
689 }
690 
691 // ************************** IODTNVRAMVariables ****************************
692 
693 // private IOService based class for publishing distinct dictionary properties on
694 // for easy ioreg access since the serializeProperties call is overloaded and is used
695 // as variable access
696 class IODTNVRAMVariables : public IOService
697 {
698 	OSDeclareDefaultStructors(IODTNVRAMVariables)
699 private:
700 	IODTNVRAM        *_provider;
701 	uuid_t           _guid;
702 
703 public:
704 	bool                    init(const uuid_t guid);
705 	virtual bool            start(IOService * provider) APPLE_KEXT_OVERRIDE;
706 
707 	virtual bool            serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
708 	virtual OSPtr<OSObject> copyProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
709 	virtual OSObject        *getProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
710 	virtual bool            setProperty(const OSSymbol *aKey, OSObject *anObject) APPLE_KEXT_OVERRIDE;
711 	virtual IOReturn        setProperties(OSObject *properties) APPLE_KEXT_OVERRIDE;
712 	virtual void            removeProperty(const OSSymbol *aKey) APPLE_KEXT_OVERRIDE;
713 };
714 
OSDefineMetaClassAndStructors(IODTNVRAMVariables,IOService)715 OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
716 
717 bool
718 IODTNVRAMVariables::init(const uuid_t guid)
719 {
720 	require(super::init(), fail);
721 
722 	uuid_copy(_guid, guid);
723 
724 	return true;
725 
726 fail:
727 	return false;
728 }
729 
730 bool
start(IOService * provider)731 IODTNVRAMVariables::start(IOService * provider)
732 {
733 	if (!super::start(provider)) {
734 		goto error;
735 	}
736 
737 	_provider = OSDynamicCast(IODTNVRAM, provider);
738 	if (_provider == nullptr) {
739 		goto error;
740 	}
741 
742 	registerService();
743 
744 	return true;
745 
746 error:
747 	stop(provider);
748 
749 	return false;
750 }
751 
752 bool
serializeProperties(OSSerialize * s) const753 IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
754 {
755 	const OSSymbol                    *key;
756 	OSSharedPtr<OSDictionary>         dict;
757 	OSSharedPtr<OSCollectionIterator> iter;
758 	OSSharedPtr<OSDictionary>         localVariables;
759 	bool                              ok = false;
760 	bool                              systemGuid = uuid_compare(_guid, gAppleSystemVariableGuid) == 0;
761 
762 	if (systemGuid) {
763 		localVariables = _provider->_systemDict;
764 	} else {
765 		localVariables = _provider->_commonDict;
766 	}
767 
768 	if (localVariables == nullptr) {
769 		goto exit;
770 	}
771 
772 	dict = OSDictionary::withCapacity(localVariables->getCount());
773 	if (dict == nullptr) {
774 		DEBUG_ERROR("No dictionary\n");
775 		goto exit;
776 	}
777 
778 	iter = OSCollectionIterator::withCollection(localVariables.get());
779 	if (iter == nullptr) {
780 		DEBUG_ERROR("failed to create iterator\n");
781 		goto exit;
782 	}
783 
784 	while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
785 		if (verifyPermission(kIONVRAMOperationRead, _guid, key)) {
786 			dict->setObject(key, localVariables->getObject(key));
787 		}
788 	}
789 
790 	ok = dict->serialize(s);
791 
792 exit:
793 	DEBUG_INFO("ok=%d\n", ok);
794 	return ok;
795 }
796 
797 OSPtr<OSObject>
copyProperty(const OSSymbol * aKey) const798 IODTNVRAMVariables::copyProperty(const OSSymbol *aKey) const
799 {
800 	if (_provider && !skipKey(aKey)) {
801 		DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
802 
803 		return _provider->copyPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
804 	} else {
805 		return nullptr;
806 	}
807 }
808 
809 OSObject *
getProperty(const OSSymbol * aKey) const810 IODTNVRAMVariables::getProperty(const OSSymbol *aKey) const
811 {
812 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
813 
814 	return theObject.get();
815 }
816 
817 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)818 IODTNVRAMVariables::setProperty(const OSSymbol *aKey, OSObject *anObject)
819 {
820 	if (_provider) {
821 		return _provider->setPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy(), anObject) == kIOReturnSuccess;
822 	} else {
823 		return false;
824 	}
825 }
826 
827 IOReturn
setProperties(OSObject * properties)828 IODTNVRAMVariables::setProperties(OSObject *properties)
829 {
830 	IOReturn                          ret = kIOReturnSuccess;
831 	OSObject                          *object;
832 	const OSSymbol                    *key;
833 	OSDictionary                      *dict;
834 	OSSharedPtr<OSCollectionIterator> iter;
835 
836 	if (_provider) {
837 		dict = OSDynamicCast(OSDictionary, properties);
838 		if (dict == nullptr) {
839 			DEBUG_ERROR("Not a dictionary\n");
840 			return kIOReturnBadArgument;
841 		}
842 
843 		iter = OSCollectionIterator::withCollection(dict);
844 		if (iter == nullptr) {
845 			DEBUG_ERROR("Couldn't create iterator\n");
846 			return kIOReturnBadArgument;
847 		}
848 
849 		while (ret == kIOReturnSuccess) {
850 			key = OSDynamicCast(OSSymbol, iter->getNextObject());
851 			if (key == nullptr) {
852 				break;
853 			}
854 
855 			object = dict->getObject(key);
856 			if (object == nullptr) {
857 				continue;
858 			}
859 
860 			ret = _provider->setPropertyWithGUIDAndName(_guid, key->getCStringNoCopy(), object);
861 		}
862 	} else {
863 		ret = kIOReturnNotReady;
864 	}
865 
866 	DEBUG_INFO("ret=%#08x\n", ret);
867 
868 	return ret;
869 }
870 
871 void
removeProperty(const OSSymbol * aKey)872 IODTNVRAMVariables::removeProperty(const OSSymbol *aKey)
873 {
874 	if (_provider) {
875 		_provider->removePropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
876 	}
877 }
878 
879 // ************************** Format Handlers ***************************
880 
~IODTNVRAMFormatHandler()881 IODTNVRAMFormatHandler::~IODTNVRAMFormatHandler()
882 {
883 }
884 
885 #include "IONVRAMCHRPHandler.cpp"
886 
887 #include "IONVRAMV3Handler.cpp"
888 
889 // **************************** IODTNVRAM *********************************
890 
891 bool
init(IORegistryEntry * old,const IORegistryPlane * plane)892 IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
893 {
894 	OSSharedPtr<OSDictionary> dict;
895 
896 	DEBUG_INFO("...\n");
897 
898 	require(super::init(old, plane), fail);
899 
900 #if XNU_TARGET_OS_OSX
901 #if CONFIG_CSR
902 	gInternalBuild = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0);
903 	DEBUG_INFO("gInternalBuild = %d\n", gInternalBuild);
904 #endif // CONFIG_CSR
905 #endif // XNU_TARGET_OS_OSX
906 
907 	_variableLock = IORWLockAlloc();
908 	require(_variableLock != nullptr, fail);
909 
910 	_controllerLock = IOLockAlloc();
911 	require(_controllerLock != nullptr, fail);
912 
913 	// Clear the IORegistryEntry property table
914 	dict =  OSDictionary::withCapacity(1);
915 	require(dict != nullptr, fail);
916 
917 	setPropertyTable(dict.get());
918 	dict.reset();
919 
920 	return true;
921 
922 fail:
923 	return false;
924 }
925 
926 bool
start(IOService * provider)927 IODTNVRAM::start(IOService *provider)
928 {
929 	OSSharedPtr<OSNumber> version;
930 
931 	DEBUG_INFO("...\n");
932 
933 	require(super::start(provider), fail);
934 
935 	// Check if our overridden init function was called
936 	// If not, skip any additional initialization being done here.
937 	// This is not an error we just need to successfully exit this function to allow
938 	// AppleEFIRuntime to proceed and take over operation
939 	require_action(_controllerLock != nullptr, no_common, DEBUG_INFO("x86 init\n"));
940 
941 	_diags = new IODTNVRAMDiags;
942 	if (!_diags || !_diags->init()) {
943 		DEBUG_ERROR("Unable to create/init the diags service\n");
944 		OSSafeReleaseNULL(_diags);
945 		goto fail;
946 	}
947 
948 	if (!_diags->attach(this)) {
949 		DEBUG_ERROR("Unable to attach the diags service!\n");
950 		OSSafeReleaseNULL(_diags);
951 		goto fail;
952 	}
953 
954 	if (!_diags->start(this)) {
955 		DEBUG_ERROR("Unable to start the diags service!\n");
956 		_diags->detach(this);
957 		OSSafeReleaseNULL(_diags);
958 		goto fail;
959 	}
960 
961 	// This will load the proxied variable data which will call back into
962 	// IODTNVRAM for the variable sets which will also update the system/common services
963 	initImageFormat();
964 
965 	version = OSNumber::withNumber(_format->getVersion(), 32);
966 	_diags->setProperty(kCurrentNVRAMVersionKey, version.get());
967 
968 	if (_format->getSystemUsed()) {
969 		_systemService = new IODTNVRAMVariables;
970 
971 		if (!_systemService || !_systemService->init(gAppleSystemVariableGuid)) {
972 			DEBUG_ERROR("Unable to start the system service!\n");
973 			OSSafeReleaseNULL(_systemService);
974 			goto no_system;
975 		}
976 
977 		_systemService->setName("options-system");
978 
979 		if (!_systemService->attach(this)) {
980 			DEBUG_ERROR("Unable to attach the system service!\n");
981 			OSSafeReleaseNULL(_systemService);
982 			goto no_system;
983 		}
984 
985 		if (!_systemService->start(this)) {
986 			DEBUG_ERROR("Unable to start the system service!\n");
987 			_systemService->detach(this);
988 			OSSafeReleaseNULL(_systemService);
989 			goto no_system;
990 		}
991 	}
992 
993 no_system:
994 	_commonService = new IODTNVRAMVariables;
995 
996 	if (!_commonService || !_commonService->init(gAppleNVRAMGuid)) {
997 		DEBUG_ERROR("Unable to start the common service!\n");
998 		OSSafeReleaseNULL(_commonService);
999 		goto no_common;
1000 	}
1001 
1002 	_commonService->setName("options-common");
1003 
1004 	if (!_commonService->attach(this)) {
1005 		DEBUG_ERROR("Unable to attach the common service!\n");
1006 		OSSafeReleaseNULL(_commonService);
1007 		goto no_common;
1008 	}
1009 
1010 	if (!_commonService->start(this)) {
1011 		DEBUG_ERROR("Unable to start the common service!\n");
1012 		_commonService->detach(this);
1013 		OSSafeReleaseNULL(_commonService);
1014 		goto no_common;
1015 	}
1016 
1017 no_common:
1018 	return true;
1019 
1020 fail:
1021 	stop(provider);
1022 	return false;
1023 }
1024 
1025 void
initImageFormat(void)1026 IODTNVRAM::initImageFormat(void)
1027 {
1028 	OSSharedPtr<IORegistryEntry> entry;
1029 	OSSharedPtr<OSObject>        prop;
1030 	const char                   *proxyDataKey = "nvram-proxy-data";
1031 	const char                   *bankSizeKey = "nvram-bank-size";
1032 	OSData                       *data;
1033 	uint32_t                     size = 0;
1034 
1035 	entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1036 
1037 	require(entry != nullptr, skip);
1038 
1039 	prop = entry->copyProperty(bankSizeKey);
1040 	require(prop != nullptr, skip);
1041 
1042 	data = OSDynamicCast(OSData, prop.get());
1043 	require(data != nullptr, skip);
1044 
1045 	size = *((uint32_t*)data->getBytesNoCopy());
1046 	DEBUG_ALWAYS("NVRAM size is %u bytes\n", size);
1047 
1048 	prop = entry->copyProperty(proxyDataKey);
1049 	require(prop != nullptr, skip);
1050 
1051 	data = OSDynamicCast(OSData, prop.get());
1052 	require(data != nullptr, skip);
1053 
1054 	if (IONVRAMV3Handler::isValidImage((const uint8_t *)data->getBytesNoCopy(), size)) {
1055 		_format = IONVRAMV3Handler::init(this, (const uint8_t *)data->getBytesNoCopy(), size, _commonDict, _systemDict);
1056 		require_action(_format, skip, panic("IONVRAMV3Handler creation failed\n"));
1057 	} else {
1058 		_format = IONVRAMCHRPHandler::init(this, (const uint8_t *)data->getBytesNoCopy(), size, _commonDict, _systemDict);
1059 		require_action(_format, skip, panic("IONVRAMCHRPHandler creation failed\n"));
1060 	}
1061 
1062 #if defined(RELEASE)
1063 	if (entry != nullptr) {
1064 		entry->removeProperty(proxyDataKey);
1065 	}
1066 #endif
1067 
1068 skip:
1069 	_lastDeviceSync = 0;
1070 	_freshInterval = true;
1071 }
1072 
1073 void
registerNVRAMController(IONVRAMController * controller)1074 IODTNVRAM::registerNVRAMController(IONVRAMController *controller)
1075 {
1076 	DEBUG_INFO("setting controller\n");
1077 
1078 	NVRAMWRITELOCK();
1079 	CONTROLLERLOCK();
1080 
1081 	_format->setController(controller);
1082 
1083 	CONTROLLERUNLOCK();
1084 	NVRAMUNLOCK();
1085 
1086 	return;
1087 }
1088 
1089 bool
safeToSync(void)1090 IODTNVRAM::safeToSync(void)
1091 {
1092 	AbsoluteTime delta;
1093 	UInt64       delta_ns;
1094 	SInt32       delta_secs;
1095 
1096 	// delta interval went by
1097 	clock_get_uptime(&delta);
1098 
1099 	// Figure it in seconds.
1100 	absolutetime_to_nanoseconds(delta, &delta_ns);
1101 	delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
1102 
1103 	if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
1104 		_lastDeviceSync = delta_secs;
1105 		_freshInterval = false;
1106 		return true;
1107 	}
1108 
1109 	return false;
1110 }
1111 
1112 void
syncInternal(bool rateLimit)1113 IODTNVRAM::syncInternal(bool rateLimit)
1114 {
1115 	DEBUG_INFO("rateLimit=%d\n", rateLimit);
1116 
1117 	if (!SAFE_TO_LOCK()) {
1118 		DEBUG_INFO("cannot lock\n");
1119 		return;
1120 	}
1121 
1122 	// Rate limit requests to sync. Drivers that need this rate limiting will
1123 	// shadow the data and only write to flash when they get a sync call
1124 	if (rateLimit) {
1125 		if (safeToSync() == false) {
1126 			DEBUG_INFO("safeToSync()=false\n");
1127 			return;
1128 		}
1129 	}
1130 
1131 	DEBUG_INFO("Calling sync()\n");
1132 
1133 	NVRAMREADLOCK();
1134 	CONTROLLERLOCK();
1135 
1136 	_format->sync();
1137 
1138 	CONTROLLERUNLOCK();
1139 	NVRAMUNLOCK();
1140 
1141 	if (_diags) {
1142 		OSSharedPtr<OSNumber> generation = OSNumber::withNumber(_format->getGeneration(), 32);
1143 		_diags->setProperty(kCurrentGenerationCountKey, generation.get());
1144 	}
1145 }
1146 
1147 void
sync(void)1148 IODTNVRAM::sync(void)
1149 {
1150 	syncInternal(false);
1151 }
1152 
1153 bool
serializeProperties(OSSerialize * s) const1154 IODTNVRAM::serializeProperties(OSSerialize *s) const
1155 {
1156 	const OSSymbol                    *key;
1157 	OSSharedPtr<OSDictionary>         systemDict, commonDict, dict;
1158 	OSSharedPtr<OSCollectionIterator> iter;
1159 	bool                              ok = false;
1160 	unsigned int                      totalCapacity = 0;
1161 
1162 	NVRAMREADLOCK();
1163 	if (_commonDict) {
1164 		commonDict = OSDictionary::withDictionary(_commonDict.get());
1165 	}
1166 
1167 	if (_systemDict) {
1168 		systemDict = OSDictionary::withDictionary(_systemDict.get());
1169 	}
1170 	NVRAMUNLOCK();
1171 
1172 	totalCapacity += (commonDict != nullptr) ? commonDict->getCapacity() : 0;
1173 	totalCapacity += (systemDict != nullptr) ? systemDict->getCapacity() : 0;
1174 
1175 	dict = OSDictionary::withCapacity(totalCapacity);
1176 
1177 	if (dict == nullptr) {
1178 		DEBUG_ERROR("No dictionary\n");
1179 		goto exit;
1180 	}
1181 
1182 	// Copy system entries first if present then copy unique common entries
1183 	if (systemDict != nullptr) {
1184 		iter = OSCollectionIterator::withCollection(systemDict.get());
1185 		if (iter == nullptr) {
1186 			DEBUG_ERROR("failed to create iterator\n");
1187 			goto exit;
1188 		}
1189 
1190 		while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1191 			if (verifyPermission(kIONVRAMOperationRead, gAppleSystemVariableGuid, key)) {
1192 				dict->setObject(key, systemDict->getObject(key));
1193 			}
1194 		}
1195 
1196 		iter.reset();
1197 	}
1198 
1199 	if (commonDict != nullptr) {
1200 		iter = OSCollectionIterator::withCollection(commonDict.get());
1201 		if (iter == nullptr) {
1202 			DEBUG_ERROR("failed to create common iterator\n");
1203 			goto exit;
1204 		}
1205 
1206 		while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1207 			if (dict->getObject(key) != nullptr) {
1208 				// Skip non uniques
1209 				continue;
1210 			}
1211 			if (verifyPermission(kIONVRAMOperationRead, gAppleNVRAMGuid, key)) {
1212 				dict->setObject(key, commonDict->getObject(key));
1213 			}
1214 		}
1215 	}
1216 
1217 	ok = dict->serialize(s);
1218 
1219 exit:
1220 	DEBUG_INFO("ok=%d\n", ok);
1221 
1222 	return ok;
1223 }
1224 
1225 NVRAMPartitionType
getDictionaryType(const OSDictionary * dict) const1226 IODTNVRAM::getDictionaryType(const OSDictionary *dict) const
1227 {
1228 	if (dict == _commonDict) {
1229 		return kIONVRAMPartitionCommon;
1230 	} else if (dict == _systemDict) {
1231 		return kIONVRAMPartitionSystem;
1232 	} else {
1233 		return kIONVRAMPartitionTypeUnknown;
1234 	}
1235 }
1236 
1237 IOReturn
chooseDictionary(IONVRAMOperation operation,const uuid_t varGuid,const char * variableName,OSDictionary ** dict) const1238 IODTNVRAM::chooseDictionary(IONVRAMOperation operation, const uuid_t varGuid, const char *variableName, OSDictionary **dict) const
1239 {
1240 	if (_systemDict != nullptr) {
1241 		bool systemGuid = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
1242 
1243 		if (variableInAllowList(variableName)) {
1244 			DEBUG_INFO("Using system dictionary due to allow list\n");
1245 			if (!systemGuid) {
1246 				DEBUG_ERROR("System GUID NOT used for %s\n", variableName);
1247 			}
1248 			*dict = _systemDict.get();
1249 		} else if (systemGuid) {
1250 			DEBUG_INFO("Using system dictionary via GUID\n");
1251 			*dict = _systemDict.get();
1252 		} else {
1253 			DEBUG_INFO("Using common dictionary\n");
1254 			*dict = _commonDict.get();
1255 		}
1256 		return kIOReturnSuccess;
1257 	} else if (_commonDict != nullptr) {
1258 		DEBUG_INFO("Defaulting to common dictionary\n");
1259 		*dict = _commonDict.get();
1260 		return kIOReturnSuccess;
1261 	}
1262 
1263 	return kIOReturnNotFound;
1264 }
1265 
1266 IOReturn
flushDict(const uuid_t guid,IONVRAMOperation op)1267 IODTNVRAM::flushDict(const uuid_t guid, IONVRAMOperation op)
1268 {
1269 	IOReturn ret = kIOReturnSuccess;
1270 
1271 	if ((_systemDict != nullptr) && (uuid_compare(guid, gAppleSystemVariableGuid) == 0)) {
1272 		ret = _format->flush(guid, op);
1273 
1274 		DEBUG_INFO("system dictionary flushed, ret=%08x\n", ret);
1275 	} else if ((_commonDict != nullptr) && (uuid_compare(guid, gAppleNVRAMGuid) == 0)) {
1276 		ret = _format->flush(guid, op);
1277 
1278 		DEBUG_INFO("common dictionary flushed, ret=%08x\n", ret);
1279 	}
1280 
1281 	return ret;
1282 }
1283 
1284 bool
handleSpecialVariables(const char * name,const uuid_t guid,const OSObject * obj,IOReturn * error)1285 IODTNVRAM::handleSpecialVariables(const char *name, const uuid_t guid, const OSObject *obj, IOReturn *error)
1286 {
1287 	IOReturn ret = kIOReturnSuccess;
1288 	bool special = false;
1289 
1290 	NVRAMLOCKASSERTEXCLUSIVE();
1291 
1292 	// ResetNVRam flushes both regions in one call
1293 	// Obliterate can flush either separately
1294 	if (strcmp(name, "ObliterateNVRam") == 0) {
1295 		special = true;
1296 		ret = flushDict(guid, kIONVRAMOperationObliterate);
1297 	} else if (strcmp(name, "ResetNVRam") == 0) {
1298 		special = true;
1299 		ret = flushDict(gAppleSystemVariableGuid, kIONVRAMOperationReset);
1300 
1301 		if (ret != kIOReturnSuccess) {
1302 			goto exit;
1303 		}
1304 
1305 		ret = flushDict(gAppleNVRAMGuid, kIONVRAMOperationReset);
1306 	}
1307 
1308 exit:
1309 	if (error) {
1310 		*error = ret;
1311 	}
1312 
1313 	return special;
1314 }
1315 
1316 OSSharedPtr<OSObject>
copyPropertyWithGUIDAndName(const uuid_t guid,const char * name) const1317 IODTNVRAM::copyPropertyWithGUIDAndName(const uuid_t guid, const char *name) const
1318 {
1319 	IOReturn              result;
1320 	OSDictionary          *dict;
1321 	OSSharedPtr<OSObject> theObject = nullptr;
1322 
1323 	result = chooseDictionary(kIONVRAMOperationRead, guid, name, &dict);
1324 	if (result != kIOReturnSuccess) {
1325 		DEBUG_INFO("No dictionary\n");
1326 		goto exit;
1327 	}
1328 
1329 	if (!verifyPermission(kIONVRAMOperationRead, guid, name)) {
1330 		DEBUG_INFO("Not privileged\n");
1331 		goto exit;
1332 	}
1333 
1334 	NVRAMREADLOCK();
1335 	theObject.reset(dict->getObject(name), OSRetain);
1336 	NVRAMUNLOCK();
1337 
1338 	if (_diags) {
1339 		_diags->logVariable(getDictionaryType(dict), kIONVRAMOperationRead, name, NULL);
1340 	}
1341 
1342 	if (theObject != nullptr) {
1343 		DEBUG_INFO("found data\n");
1344 	}
1345 
1346 exit:
1347 	return theObject;
1348 }
1349 
1350 OSSharedPtr<OSObject>
copyProperty(const OSSymbol * aKey) const1351 IODTNVRAM::copyProperty(const OSSymbol *aKey) const
1352 {
1353 	const char            *variableName;
1354 	uuid_t                varGuid;
1355 
1356 	if (skipKey(aKey)) {
1357 		return nullptr;
1358 	}
1359 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1360 
1361 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1362 
1363 	return copyPropertyWithGUIDAndName(varGuid, variableName);
1364 }
1365 
1366 OSSharedPtr<OSObject>
copyProperty(const char * aKey) const1367 IODTNVRAM::copyProperty(const char *aKey) const
1368 {
1369 	OSSharedPtr<const OSSymbol> keySymbol;
1370 	OSSharedPtr<OSObject>       theObject;
1371 
1372 	keySymbol = OSSymbol::withCString(aKey);
1373 	if (keySymbol != nullptr) {
1374 		theObject = copyProperty(keySymbol.get());
1375 	}
1376 
1377 	return theObject;
1378 }
1379 
1380 OSObject *
getProperty(const OSSymbol * aKey) const1381 IODTNVRAM::getProperty(const OSSymbol *aKey) const
1382 {
1383 	// The shared pointer gets released at the end of the function,
1384 	// and returns a view into theObject.
1385 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1386 
1387 	return theObject.get();
1388 }
1389 
1390 OSObject *
getProperty(const char * aKey) const1391 IODTNVRAM::getProperty(const char *aKey) const
1392 {
1393 	// The shared pointer gets released at the end of the function,
1394 	// and returns a view into theObject.
1395 	OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1396 
1397 	return theObject.get();
1398 }
1399 
1400 IOReturn
setPropertyWithGUIDAndName(const uuid_t guid,const char * name,OSObject * anObject)1401 IODTNVRAM::setPropertyWithGUIDAndName(const uuid_t guid, const char *name, OSObject *anObject)
1402 {
1403 	IOReturn              ret = kIOReturnSuccess;
1404 	bool                  remove = false;
1405 	OSString              *tmpString = nullptr;
1406 	OSSharedPtr<OSObject> propObject;
1407 	OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
1408 	bool                  deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
1409 	bool                  ok;
1410 	size_t                propDataSize = 0;
1411 
1412 	deletePropertyKey = strncmp(name, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
1413 	syncNowPropertyKey = strncmp(name, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
1414 	forceSyncNowPropertyKey = strncmp(name, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
1415 
1416 	if (deletePropertyKey) {
1417 		tmpString = OSDynamicCast(OSString, anObject);
1418 		if (tmpString != nullptr) {
1419 			const char *variableName;
1420 			uuid_t     valueVarGuid;
1421 			bool       guidProvided;
1422 			IOReturn   removeRet;
1423 
1424 			guidProvided = parseVariableName(tmpString->getCStringNoCopy(), &valueVarGuid, &variableName);
1425 
1426 			// nvram tool will provide a "nvram -d var" or "nvram -d guid:var" as
1427 			// kIONVRAMDeletePropertyKey=var or kIONVRAMDeletePropertyKey=guid:var
1428 			// that will come into this function as (gAppleNVRAMGuid, varname, nullptr)
1429 			// if we provide the "-z" flag to the nvram tool this function will come in as
1430 			// (gAppleSystemVariableGuid, varname, nullptr). We are reparsing the value string,
1431 			// if there is a GUID provided with the value then use that GUID otherwise use the
1432 			// guid that was provided via the node selection or default.
1433 			if (guidProvided == false) {
1434 				DEBUG_INFO("Removing with API provided GUID\n");
1435 				removeRet = removePropertyWithGUIDAndName(guid, variableName);
1436 			} else {
1437 				DEBUG_INFO("Removing with value provided GUID\n");
1438 				removeRet = removePropertyWithGUIDAndName(valueVarGuid, variableName);
1439 			}
1440 
1441 			DEBUG_INFO("kIONVRAMDeletePropertyKey found, removeRet=%#08x\n", removeRet);
1442 		} else {
1443 			DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1444 			ret = kIOReturnError;
1445 		}
1446 		goto exit;
1447 	} else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
1448 		tmpString = OSDynamicCast(OSString, anObject);
1449 		DEBUG_INFO("NVRAM sync key %s found\n", name);
1450 		if (tmpString != nullptr) {
1451 			// We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1452 			syncInternal(syncNowPropertyKey);
1453 		} else {
1454 			DEBUG_INFO("%s value needs to be an OSString\n", name);
1455 			ret = kIOReturnError;
1456 		}
1457 		goto exit;
1458 	}
1459 
1460 	if (!verifyPermission(kIONVRAMOperationWrite, guid, name)) {
1461 		DEBUG_INFO("Not privileged\n");
1462 		ret = kIOReturnNotPrivileged;
1463 		goto exit;
1464 	}
1465 
1466 	// Make sure the object is of the correct type.
1467 	switch (getVariableType(name)) {
1468 	case kOFVariableTypeBoolean:
1469 		propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
1470 		break;
1471 
1472 	case kOFVariableTypeNumber:
1473 		propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
1474 		break;
1475 
1476 	case kOFVariableTypeString:
1477 		propObject = OSDynamicPtrCast<OSString>(sharedObject);
1478 		if (propObject != nullptr) {
1479 			propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
1480 
1481 			if ((strncmp(name, kIONVRAMBootArgsKey, sizeof(kIONVRAMBootArgsKey)) == 0) && (propDataSize >= BOOT_LINE_LENGTH)) {
1482 				DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
1483 				ret = kIOReturnNoSpace;
1484 				goto exit;
1485 			}
1486 		}
1487 		break;
1488 
1489 	case kOFVariableTypeData:
1490 		propObject = OSDynamicPtrCast<OSData>(sharedObject);
1491 		if (propObject == nullptr) {
1492 			tmpString = OSDynamicCast(OSString, sharedObject.get());
1493 			if (tmpString != nullptr) {
1494 				propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
1495 				    tmpString->getLength());
1496 			}
1497 		}
1498 
1499 		if (propObject != nullptr) {
1500 			propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
1501 		}
1502 
1503 #if defined(XNU_TARGET_OS_OSX)
1504 		if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
1505 			remove = true;
1506 		}
1507 #endif /* defined(XNU_TARGET_OS_OSX) */
1508 		break;
1509 	default:
1510 		break;
1511 	}
1512 
1513 	if (propObject == nullptr) {
1514 		DEBUG_INFO("No property object\n");
1515 		ret = kIOReturnBadArgument;
1516 		goto exit;
1517 	}
1518 
1519 	if (!verifyWriteSizeLimit(guid, name, propDataSize)) {
1520 		DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, name);
1521 		ret = kIOReturnNoSpace;
1522 		goto exit;
1523 	}
1524 
1525 	NVRAMWRITELOCK();
1526 	ok = handleSpecialVariables(name, guid, propObject.get(), &ret);
1527 	NVRAMUNLOCK();
1528 
1529 	if (ok) {
1530 		goto exit;
1531 	}
1532 
1533 	if (remove == false) {
1534 		DEBUG_INFO("Adding object\n");
1535 		NVRAMWRITELOCK();
1536 		ret = _format->setVariable(guid, name, propObject.get());
1537 		NVRAMUNLOCK();
1538 	} else {
1539 		DEBUG_INFO("Removing object\n");
1540 		ret = removePropertyWithGUIDAndName(guid, name);
1541 	}
1542 
1543 	if (tmpString) {
1544 		propObject.reset();
1545 	}
1546 
1547 exit:
1548 	DEBUG_INFO("ret=%#08x\n", ret);
1549 
1550 	return ret;
1551 }
1552 
1553 IOReturn
setPropertyInternal(const OSSymbol * aKey,OSObject * anObject)1554 IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
1555 {
1556 	const char *variableName;
1557 	uuid_t     varGuid;
1558 
1559 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1560 
1561 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1562 
1563 	return setPropertyWithGUIDAndName(varGuid, variableName, anObject);
1564 }
1565 
1566 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)1567 IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
1568 {
1569 	return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
1570 }
1571 
1572 void
removeProperty(const OSSymbol * aKey)1573 IODTNVRAM::removeProperty(const OSSymbol *aKey)
1574 {
1575 	IOReturn ret;
1576 
1577 	ret = removePropertyInternal(aKey);
1578 
1579 	if (ret != kIOReturnSuccess) {
1580 		DEBUG_INFO("removePropertyInternal failed, ret=%#08x\n", ret);
1581 	}
1582 }
1583 
1584 IOReturn
removePropertyWithGUIDAndName(const uuid_t guid,const char * name)1585 IODTNVRAM::removePropertyWithGUIDAndName(const uuid_t guid, const char *name)
1586 {
1587 	IOReturn      ret = kIOReturnSuccess;
1588 	uuid_string_t uuidString;
1589 
1590 	uuid_unparse(guid, uuidString);
1591 
1592 	DEBUG_INFO("%s:%s\n", uuidString, name);
1593 
1594 	if (!verifyPermission(kIONVRAMOperationDelete, guid, name)) {
1595 		DEBUG_INFO("Not priveleged\n");
1596 		ret = kIOReturnNotPrivileged;
1597 		goto exit;
1598 	}
1599 
1600 	NVRAMWRITELOCK();
1601 
1602 	if (_format->setVariable(guid, name, nullptr) != kIOReturnSuccess) {
1603 		DEBUG_INFO("%s not found\n", name);
1604 		ret = kIOReturnNotFound;
1605 	}
1606 
1607 	NVRAMUNLOCK();
1608 
1609 exit:
1610 	return ret;
1611 }
1612 
1613 IOReturn
removePropertyInternal(const OSSymbol * aKey)1614 IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
1615 {
1616 	IOReturn   ret;
1617 	const char *variableName;
1618 	uuid_t     varGuid;
1619 
1620 	DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1621 
1622 	parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1623 
1624 	ret = removePropertyWithGUIDAndName(varGuid, variableName);
1625 
1626 	return ret;
1627 }
1628 
1629 IOReturn
setProperties(OSObject * properties)1630 IODTNVRAM::setProperties(OSObject *properties)
1631 {
1632 	IOReturn                          ret = kIOReturnSuccess;
1633 	OSObject                          *object;
1634 	const OSSymbol                    *key;
1635 	OSDictionary                      *dict;
1636 	OSSharedPtr<OSCollectionIterator> iter;
1637 
1638 	dict = OSDynamicCast(OSDictionary, properties);
1639 	if (dict == nullptr) {
1640 		DEBUG_ERROR("Not a dictionary\n");
1641 		return kIOReturnBadArgument;
1642 	}
1643 
1644 	iter = OSCollectionIterator::withCollection(dict);
1645 	if (iter == nullptr) {
1646 		DEBUG_ERROR("Couldn't create iterator\n");
1647 		return kIOReturnBadArgument;
1648 	}
1649 
1650 	while (ret == kIOReturnSuccess) {
1651 		key = OSDynamicCast(OSSymbol, iter->getNextObject());
1652 		if (key == nullptr) {
1653 			break;
1654 		}
1655 
1656 		object = dict->getObject(key);
1657 		if (object == nullptr) {
1658 			continue;
1659 		}
1660 
1661 		ret = setPropertyInternal(key, object);
1662 	}
1663 
1664 	DEBUG_INFO("ret=%#08x\n", ret);
1665 
1666 	return ret;
1667 }
1668 
1669 // ********************** Deprecated ********************
1670 
1671 IOReturn
readXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)1672 IODTNVRAM::readXPRAM(IOByteCount offset, uint8_t *buffer,
1673     IOByteCount length)
1674 {
1675 	return kIOReturnUnsupported;
1676 }
1677 
1678 IOReturn
writeXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)1679 IODTNVRAM::writeXPRAM(IOByteCount offset, uint8_t *buffer,
1680     IOByteCount length)
1681 {
1682 	return kIOReturnUnsupported;
1683 }
1684 
1685 IOReturn
readNVRAMProperty(IORegistryEntry * entry,const OSSymbol ** name,OSData ** value)1686 IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
1687     const OSSymbol **name,
1688     OSData **value)
1689 {
1690 	return kIOReturnUnsupported;
1691 }
1692 
1693 IOReturn
writeNVRAMProperty(IORegistryEntry * entry,const OSSymbol * name,OSData * value)1694 IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
1695     const OSSymbol *name,
1696     OSData *value)
1697 {
1698 	return kIOReturnUnsupported;
1699 }
1700 
1701 OSDictionary *
getNVRAMPartitions(void)1702 IODTNVRAM::getNVRAMPartitions(void)
1703 {
1704 	return NULL;
1705 }
1706 
1707 IOReturn
readNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)1708 IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
1709     IOByteCount offset, uint8_t *buffer,
1710     IOByteCount length)
1711 {
1712 	return kIOReturnUnsupported;
1713 }
1714 
1715 IOReturn
writeNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)1716 IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
1717     IOByteCount offset, uint8_t *buffer,
1718     IOByteCount length)
1719 {
1720 	return kIOReturnUnsupported;
1721 }
1722 
1723 IOByteCount
savePanicInfo(uint8_t * buffer,IOByteCount length)1724 IODTNVRAM::savePanicInfo(uint8_t *buffer, IOByteCount length)
1725 {
1726 	return 0;
1727 }
1728