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