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