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