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