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