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