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