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