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 <sys/csr.h>
42
43 #define super IOService
44
45 OSDefineMetaClassAndStructors(IODTNVRAM, IOService);
46
47 class IONVRAMCHRPHandler;
48 class IONVRAMV3Handler;
49
50 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
51
52 #define MAX_VAR_NAME_SIZE 63
53
54 #define kCurrentGenerationCountKey "Generation"
55 #define kCurrentNVRAMVersionKey "Version"
56
57 #define kNVRAMCommonUsedKey "CommonUsed"
58 #define kNVRAMSystemUsedKey "SystemUsed"
59
60 #define kIONVRAMPrivilege kIOClientPrivilegeAdministrator
61
62 #define MIN_SYNC_NOW_INTERVAL 15*60 /* Minimum 15 Minutes interval mandated */
63
64 #if defined(DEBUG) || defined(DEVELOPMENT)
65 #define DEBUG_INFO(fmt, args...) \
66 ({ \
67 if (gNVRAMLogging) \
68 IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
69 })
70
71 #define DEBUG_ALWAYS(fmt, args...) \
72 ({ \
73 IOLog("%s:%s:%u - " fmt, __FILE_NAME__, __FUNCTION__, __LINE__, ##args); \
74 })
75 #else
76 #define DEBUG_INFO(fmt, args...) (void)NULL
77 #define DEBUG_ALWAYS(fmt, args...) (void)NULL
78 #endif
79
80 #define DEBUG_ERROR DEBUG_ALWAYS
81
82 #define SAFE_TO_LOCK() (preemption_enabled() && !panic_active())
83
84 #define CONTROLLERLOCK() \
85 ({ \
86 if (SAFE_TO_LOCK()) \
87 IOLockLock(_controllerLock); \
88 })
89
90 #define CONTROLLERUNLOCK() \
91 ({ \
92 if (SAFE_TO_LOCK()) \
93 IOLockUnlock(_controllerLock); \
94 })
95
96 #define NVRAMREADLOCK() \
97 ({ \
98 if (SAFE_TO_LOCK()) \
99 IORWLockRead(_variableLock); \
100 })
101
102 #define NVRAMWRITELOCK() \
103 ({ \
104 if (SAFE_TO_LOCK()) \
105 IORWLockWrite(_variableLock); \
106 })
107
108 #define NVRAMUNLOCK() \
109 ({ \
110 if (SAFE_TO_LOCK()) \
111 IORWLockUnlock(_variableLock); \
112 })
113
114 #define NVRAMLOCKASSERTHELD() \
115 ({ \
116 if (SAFE_TO_LOCK()) \
117 IORWLockAssert(_variableLock, kIORWLockAssertHeld); \
118 })
119
120 #define NVRAMLOCKASSERTEXCLUSIVE() \
121 ({ \
122 if (SAFE_TO_LOCK()) \
123 IORWLockAssert(_variableLock, kIORWLockAssertWrite); \
124 })
125
126 enum NVRAMVersion {
127 kNVRAMVersionUnknown,
128 kNVRAMVersion1, // Legacy, banks, 0x800 common partition size
129 kNVRAMVersion2, // V1 but with (0x2000 - sizeof(struct apple_nvram_header) - sizeof(struct chrp_nvram_header)) common region
130 kNVRAMVersion3, // New EFI based format
131 kNVRAMVersionMax
132 };
133
134 // Guid for Apple System Boot variables
135 // 40A0DDD2-77F8-4392-B4A3-1E7304206516
136 UUID_DEFINE(gAppleSystemVariableGuid, 0x40, 0xA0, 0xDD, 0xD2, 0x77, 0xF8, 0x43, 0x92, 0xB4, 0xA3, 0x1E, 0x73, 0x04, 0x20, 0x65, 0x16);
137
138 // Apple NVRAM Variable namespace (APPLE_VENDOR_OS_VARIABLE_GUID)
139 // 7C436110-AB2A-4BBB-A880-FE41995C9F82
140 UUID_DEFINE(gAppleNVRAMGuid, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82);
141
142 static TUNABLE(bool, gNVRAMLogging, "nvram-log", false);
143 static bool gInternalBuild = false;
144
145 // allowlist variables from macboot that need to be set/get from system region if present
146 static const char * const gNVRAMSystemList[] = {
147 "allow-root-hash-mismatch",
148 "auto-boot",
149 "auto-boot-halt-stage",
150 "base-system-path",
151 "boot-args",
152 "boot-command",
153 "boot-image",
154 "bootdelay",
155 "com.apple.System.boot-nonce",
156 "darkboot",
157 "emu",
158 "one-time-boot-command", // Needed for diags customer install flows
159 "policy-nonce-digests",
160 "prevent-restores", // Keep for factory <rdar://problem/70476321>
161 "prev-lang:kbd",
162 "root-live-fs",
163 "sep-debug-args", // Needed to simplify debug flows for SEP
164 "StartupMute", // Set by customers via nvram tool
165 "SystemAudioVolume",
166 "SystemAudioVolumeExtension",
167 "SystemAudioVolumeSaved",
168 nullptr
169 };
170
171 typedef struct {
172 const char *name;
173 IONVRAMVariableType type;
174 } VariableTypeEntry;
175
176 static const
177 VariableTypeEntry gVariableTypes[] = {
178 {"auto-boot?", kOFVariableTypeBoolean},
179 {"boot-args", kOFVariableTypeString},
180 {"boot-command", kOFVariableTypeString},
181 {"boot-device", kOFVariableTypeString},
182 {"boot-file", kOFVariableTypeString},
183 {"boot-screen", kOFVariableTypeString},
184 {"boot-script", kOFVariableTypeString},
185 {"console-screen", kOFVariableTypeString},
186 {"default-client-ip", kOFVariableTypeString},
187 {"default-gateway-ip", kOFVariableTypeString},
188 {"default-mac-address?", kOFVariableTypeBoolean},
189 {"default-router-ip", kOFVariableTypeString},
190 {"default-server-ip", kOFVariableTypeString},
191 {"default-subnet-mask", kOFVariableTypeString},
192 {"diag-device", kOFVariableTypeString},
193 {"diag-file", kOFVariableTypeString},
194 {"diag-switch?", kOFVariableTypeBoolean},
195 {"fcode-debug?", kOFVariableTypeBoolean},
196 {"input-device", kOFVariableTypeString},
197 {"input-device-1", kOFVariableTypeString},
198 {"little-endian?", kOFVariableTypeBoolean},
199 {"load-base", kOFVariableTypeNumber},
200 {"mouse-device", kOFVariableTypeString},
201 {"nvramrc", kOFVariableTypeString},
202 {"oem-banner", kOFVariableTypeString},
203 {"oem-banner?", kOFVariableTypeBoolean},
204 {"oem-logo", kOFVariableTypeString},
205 {"oem-logo?", kOFVariableTypeBoolean},
206 {"output-device", kOFVariableTypeString},
207 {"output-device-1", kOFVariableTypeString},
208 {"pci-probe-list", kOFVariableTypeNumber},
209 {"pci-probe-mask", kOFVariableTypeNumber},
210 {"real-base", kOFVariableTypeNumber},
211 {"real-mode?", kOFVariableTypeBoolean},
212 {"real-size", kOFVariableTypeNumber},
213 {"screen-#columns", kOFVariableTypeNumber},
214 {"screen-#rows", kOFVariableTypeNumber},
215 {"security-mode", kOFVariableTypeString},
216 {"selftest-#megs", kOFVariableTypeNumber},
217 {"use-generic?", kOFVariableTypeBoolean},
218 {"use-nvramrc?", kOFVariableTypeBoolean},
219 {"virt-base", kOFVariableTypeNumber},
220 {"virt-size", kOFVariableTypeNumber},
221
222 #if !defined(__x86_64__)
223 {"acc-cm-override-charger-count", kOFVariableTypeNumber},
224 {"acc-cm-override-count", kOFVariableTypeNumber},
225 {"acc-mb-ld-lifetime", kOFVariableTypeNumber},
226 {"com.apple.System.boot-nonce", kOFVariableTypeString},
227 {"darkboot", kOFVariableTypeBoolean},
228 {"enter-tdm-mode", kOFVariableTypeBoolean},
229 #endif /* !defined(__x86_64__) */
230 {nullptr, kOFVariableTypeData} // Default type to return
231 };
232
233 union VariablePermission {
234 struct {
235 uint64_t UserWrite :1;
236 uint64_t RootRequired :1;
237 uint64_t KernelOnly :1;
238 uint64_t ResetNVRAMOnlyDelete :1;
239 uint64_t NeverAllowedToDelete :1;
240 uint64_t SystemReadHidden :1;
241 uint64_t FullAccess :1;
242 uint64_t Reserved:57;
243 } Bits;
244 uint64_t Uint64;
245 };
246
247 typedef struct {
248 const char *name;
249 VariablePermission p;
250 } VariablePermissionEntry;
251
252 static const
253 VariablePermissionEntry gVariablePermissions[] = {
254 {"aapl,pci", .p.Bits.RootRequired = 1},
255 {"battery-health", .p.Bits.RootRequired = 1,
256 .p.Bits.NeverAllowedToDelete = 1},
257 {"boot-image", .p.Bits.UserWrite = 1},
258 {"com.apple.System.fp-state", .p.Bits.KernelOnly = 1},
259 {"fm-account-masked", .p.Bits.RootRequired = 1,
260 .p.Bits.NeverAllowedToDelete = 1},
261 {"fm-activation-locked", .p.Bits.RootRequired = 1,
262 .p.Bits.NeverAllowedToDelete = 1},
263 {"fm-spkeys", .p.Bits.RootRequired = 1,
264 .p.Bits.NeverAllowedToDelete = 1},
265 {"fm-spstatus", .p.Bits.RootRequired = 1,
266 .p.Bits.NeverAllowedToDelete = 1},
267 {"policy-nonce-digests", .p.Bits.ResetNVRAMOnlyDelete = 1}, // Deleting this via user triggered obliterate leave J273a unable to boot
268 {"recoveryos-passcode-blob", .p.Bits.SystemReadHidden = 1},
269 {"security-password", .p.Bits.RootRequired = 1},
270 {"system-passcode-lock-blob", .p.Bits.SystemReadHidden = 1},
271
272 #if !defined(__x86_64__)
273 {"acc-cm-override-charger-count", .p.Bits.KernelOnly = 1},
274 {"acc-cm-override-count", .p.Bits.KernelOnly = 1},
275 {"acc-mb-ld-lifetime", .p.Bits.KernelOnly = 1},
276 {"backlight-level", .p.Bits.UserWrite = 1},
277 {"backlight-nits", .p.Bits.UserWrite = 1},
278 {"com.apple.System.boot-nonce", .p.Bits.KernelOnly = 1},
279 {"com.apple.System.sep.art", .p.Bits.KernelOnly = 1},
280 {"darkboot", .p.Bits.UserWrite = 1},
281 {"nonce-seeds", .p.Bits.KernelOnly = 1},
282 #endif /* !defined(__x86_64__) */
283
284 {nullptr, {.Bits.FullAccess = 1}} // Default access
285 };
286
287 static NVRAMPartitionType
getPartitionTypeForGUID(const uuid_t * guid)288 getPartitionTypeForGUID(const uuid_t *guid)
289 {
290 if (uuid_compare(*guid, gAppleNVRAMGuid) == 0) {
291 return kIONVRAMPartitionCommon;
292 } else if (uuid_compare(*guid, gAppleSystemVariableGuid) == 0) {
293 return kIONVRAMPartitionSystem;
294 } else {
295 return kIONVRAMPartitionTypeUnknown;
296 }
297 }
298
299 static IONVRAMVariableType
getVariableType(const char * propName)300 getVariableType(const char *propName)
301 {
302 const VariableTypeEntry *entry;
303
304 entry = gVariableTypes;
305 while (entry->name != nullptr) {
306 if (strcmp(entry->name, propName) == 0) {
307 break;
308 }
309 entry++;
310 }
311
312 return entry->type;
313 }
314
315 static IONVRAMVariableType
getVariableType(const OSSymbol * propSymbol)316 getVariableType(const OSSymbol *propSymbol)
317 {
318 return getVariableType(propSymbol->getCStringNoCopy());
319 }
320
321 static VariablePermission
getVariablePermission(const char * propName)322 getVariablePermission(const char *propName)
323 {
324 const VariablePermissionEntry *entry;
325
326 entry = gVariablePermissions;
327 while (entry->name != nullptr) {
328 if (strcmp(entry->name, propName) == 0) {
329 break;
330 }
331 entry++;
332 }
333
334 return entry->p;
335 }
336
337 static bool
variableInAllowList(const char * varName)338 variableInAllowList(const char *varName)
339 {
340 unsigned int i = 0;
341
342 while (gNVRAMSystemList[i] != nullptr) {
343 if (strcmp(varName, gNVRAMSystemList[i]) == 0) {
344 return true;
345 }
346 i++;
347 }
348
349 return false;
350 }
351
352 static bool
verifyWriteSizeLimit(const uuid_t * varGuid,const char * variableName,size_t propDataSize)353 verifyWriteSizeLimit(const uuid_t *varGuid, const char *variableName, size_t propDataSize)
354 {
355 if (variableInAllowList(variableName)) {
356 if (strnstr(variableName, "breadcrumbs", strlen(variableName)) != NULL) {
357 return propDataSize <= 1024;
358 } else {
359 return propDataSize <= 768;
360 }
361 }
362
363 return true;
364 }
365
366 #if defined(DEBUG) || defined(DEVELOPMENT)
367 static const char *
getNVRAMOpString(IONVRAMOperation op)368 getNVRAMOpString(IONVRAMOperation op)
369 {
370 switch (op) {
371 case kIONVRAMOperationRead:
372 return "Read";
373 case kIONVRAMOperationWrite:
374 return "Write";
375 case kIONVRAMOperationDelete:
376 return "Delete";
377 case kIONVRAMOperationObliterate:
378 return "Obliterate";
379 case kIONVRAMOperationReset:
380 return "Reset";
381 case kIONVRAMOperationInit:
382 return "Init";
383 default:
384 return "Unknown";
385 }
386 }
387 #endif
388
389 static bool
verifyPermission(IONVRAMOperation op,const uuid_t * varGuid,const char * varName)390 verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const char *varName)
391 {
392 VariablePermission perm;
393 bool kernel, writeEntitled = false, readEntitled = false, allowList, systemGuid = false, systemEntitled = false, systemInternalEntitled = false, systemAllow, systemReadHiddenAllow = false;
394 bool admin = false;
395 bool ok = false;
396
397 perm = getVariablePermission(varName);
398
399 kernel = current_task() == kernel_task;
400
401 if (perm.Bits.KernelOnly) {
402 DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
403 ok = kernel;
404 goto exit;
405 }
406
407 allowList = variableInAllowList(varName);
408 systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0;
409 admin = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess;
410 writeEntitled = IOCurrentTaskHasEntitlement(kIONVRAMWriteAccessKey);
411 readEntitled = IOCurrentTaskHasEntitlement(kIONVRAMReadAccessKey);
412 systemEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemAllowKey);
413 systemInternalEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemInternalAllowKey);
414 systemReadHiddenAllow = IOCurrentTaskHasEntitlement(kIONVRAMSystemHiddenAllowKey);
415
416 systemAllow = systemEntitled || (systemInternalEntitled && gInternalBuild) || kernel;
417
418 switch (op) {
419 case kIONVRAMOperationRead:
420 if (systemGuid && perm.Bits.SystemReadHidden) {
421 ok = systemReadHiddenAllow;
422 } else if (kernel || admin || readEntitled || perm.Bits.FullAccess) {
423 ok = true;
424 }
425 break;
426
427 case kIONVRAMOperationWrite:
428 if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
429 if (systemGuid) {
430 if (allowList) {
431 if (!systemAllow) {
432 DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
433 }
434 } else if (!systemAllow) {
435 DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
436 break;
437 }
438 }
439 ok = true;
440 }
441 break;
442
443 case kIONVRAMOperationDelete:
444 case kIONVRAMOperationObliterate:
445 case kIONVRAMOperationReset:
446 if (perm.Bits.NeverAllowedToDelete) {
447 DEBUG_INFO("Never allowed to delete %s\n", varName);
448 break;
449 } else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) {
450 DEBUG_INFO("Not allowed to obliterate %s\n", varName);
451 break;
452 } else if ((op == kIONVRAMOperationDelete) && perm.Bits.ResetNVRAMOnlyDelete) {
453 DEBUG_INFO("Only allowed to delete %s via NVRAM reset\n", varName);
454 break;
455 }
456
457 if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
458 if (systemGuid) {
459 if (allowList) {
460 if (!systemAllow) {
461 DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
462 }
463 } else if (!systemAllow) {
464 DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
465 break;
466 }
467 }
468 ok = true;
469 }
470 break;
471
472 case kIONVRAMOperationInit:
473 break;
474 }
475
476 exit:
477 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\n", getNVRAMOpString(op), varName, ok ? "granted" : "denied",
478 kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled, systemInternalEntitled, systemReadHiddenAllow, perm.Bits.UserWrite);
479
480 return ok;
481 }
482
483 static bool
verifyPermission(IONVRAMOperation op,const uuid_t * varGuid,const OSSymbol * varName)484 verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const OSSymbol *varName)
485 {
486 return verifyPermission(op, varGuid, varName->getCStringNoCopy());
487 }
488
489 /*
490 * Parse a variable name of the form "GUID:name".
491 * If the name cannot be parsed, substitute the Apple global variable GUID.
492 * Returns TRUE if a GUID was found in the name, FALSE otherwise.
493 * The guidResult and nameResult arguments may be nullptr if you just want
494 * to check the format of the string.
495 */
496 static bool
parseVariableName(const char * key,uuid_t * guidResult,const char ** nameResult)497 parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult)
498 {
499 uuid_string_t temp = {0};
500 size_t keyLen = strlen(key);
501 bool ok = false;
502 const char *name = key;
503 uuid_t guid;
504
505 if (keyLen > sizeof(temp)) {
506 // check for at least UUID + ":" + more
507 memcpy(temp, key, sizeof(temp) - 1);
508
509 if ((uuid_parse(temp, guid) == 0) &&
510 (key[sizeof(temp) - 1] == ':')) {
511 name = key + sizeof(temp);
512 ok = true;
513 }
514 }
515
516 if (guidResult) {
517 ok ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
518 }
519 if (nameResult) {
520 *nameResult = name;
521 }
522
523 return false;
524 }
525
526 static bool
skipKey(const OSSymbol * aKey)527 skipKey(const OSSymbol *aKey)
528 {
529 return aKey->isEqualTo(kIOClassNameOverrideKey) ||
530 aKey->isEqualTo(kIOBSDNameKey) ||
531 aKey->isEqualTo(kIOBSDNamesKey) ||
532 aKey->isEqualTo(kIOBSDMajorKey) ||
533 aKey->isEqualTo(kIOBSDMinorKey) ||
534 aKey->isEqualTo(kIOBSDUnitKey) ||
535 aKey->isEqualTo(kIOUserServicePropertiesKey) ||
536 aKey->isEqualTo(kIOMatchCategoryKey);
537 }
538
539
540 // ************************** IODTNVRAMDiags ****************************
541
542 #define kIODTNVRAMDiagsStatsKey "Stats"
543 #define kIODTNVRAMDiagsInitKey "Init"
544 #define kIODTNVRAMDiagsReadKey "Read"
545 #define kIODTNVRAMDiagsWriteKey "Write"
546 #define kIODTNVRAMDiagsDeleteKey "Delete"
547 #define kIODTNVRAMDiagsNameKey "Name"
548 #define kIODTNVRAMDiagsSizeKey "Size"
549 #define kIODTNVRAMDiagsPresentKey "Present"
550
551 // private IOService based class for publishing diagnostic info for IODTNVRAM
552 class IODTNVRAMDiags : public IOService
553 {
554 OSDeclareDefaultStructors(IODTNVRAMDiags)
555 private:
556 IODTNVRAM *_provider;
557 IORWLock *_variableLock;
558 OSSharedPtr<OSDictionary> _stats;
559
560 bool serializeStats(void *, OSSerialize * serializer);
561
562 public:
563 bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
564 void logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data);
565 };
566
OSDefineMetaClassAndStructors(IODTNVRAMDiags,IOService)567 OSDefineMetaClassAndStructors(IODTNVRAMDiags, IOService)
568
569 bool
570 IODTNVRAMDiags::start(IOService * provider)
571 {
572 OSSharedPtr<OSSerializer> serializer;
573
574 require(super::start(provider), error);
575
576 _variableLock = IORWLockAlloc();
577 require(_variableLock != nullptr, error);
578
579 _provider = OSDynamicCast(IODTNVRAM, provider);
580 require(_provider != nullptr, error);
581
582 _stats = OSDictionary::withCapacity(1);
583 require(_stats != nullptr, error);
584
585 serializer = OSSerializer::forTarget(this, OSMemberFunctionCast(OSSerializerCallback, this, &IODTNVRAMDiags::serializeStats));
586 require(serializer != nullptr, error);
587
588 setProperty(kIODTNVRAMDiagsStatsKey, serializer.get());
589
590 registerService();
591
592 return true;
593
594 error:
595 stop(provider);
596
597 return false;
598 }
599
600 void
logVariable(NVRAMPartitionType region,IONVRAMOperation op,const char * name,void * data)601 IODTNVRAMDiags::logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data)
602 {
603 // "Stats" : OSDictionary
604 // - "XX:varName" : OSDictionary, XX is the region value prefix to distinguish which dictionary the variable is in
605 // - "Init" : OSBoolean True/present if variable present at initialization
606 // - "Read" : OSNumber count
607 // - "Write" : OSNumber count
608 // - "Delete" : OSNumber count
609 // - "Size" : OSNumber size, latest size from either init or write
610 // - "Present" : OSBoolean True/False if variable is present or not
611 char *entryKey;
612 size_t entryKeySize;
613 OSSharedPtr<OSDictionary> existingEntry;
614 OSSharedPtr<OSNumber> currentCount;
615 OSSharedPtr<OSNumber> varSize;
616 const char *opCountKey = nullptr;
617
618 entryKeySize = strlen("XX:") + strlen(name) + 1;
619 entryKey = IONewData(char, entryKeySize);
620 require(entryKey, exit);
621
622 snprintf(entryKey, entryKeySize, "%02X:%s", region, name);
623
624 NVRAMWRITELOCK();
625 existingEntry.reset(OSDynamicCast(OSDictionary, _stats->getObject(entryKey)), OSRetain);
626
627 if (existingEntry == nullptr) {
628 existingEntry = OSDictionary::withCapacity(4);
629 }
630
631 switch (op) {
632 case kIONVRAMOperationRead:
633 opCountKey = kIODTNVRAMDiagsReadKey;
634 if (existingEntry->getObject(kIODTNVRAMDiagsPresentKey) == nullptr) {
635 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
636 }
637 break;
638 case kIONVRAMOperationWrite:
639 opCountKey = kIODTNVRAMDiagsWriteKey;
640 varSize = OSNumber::withNumber((size_t)data, 64);
641 existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
642 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
643 break;
644 case kIONVRAMOperationDelete:
645 case kIONVRAMOperationObliterate:
646 case kIONVRAMOperationReset:
647 opCountKey = kIODTNVRAMDiagsDeleteKey;
648 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
649 break;
650 case kIONVRAMOperationInit:
651 varSize = OSNumber::withNumber((size_t)data, 64);
652 existingEntry->setObject(kIODTNVRAMDiagsInitKey, varSize);
653 existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
654 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
655 break;
656 default:
657 goto exit;
658 }
659
660 if (opCountKey) {
661 currentCount.reset(OSDynamicCast(OSNumber, existingEntry->getObject(opCountKey)), OSRetain);
662
663 if (currentCount == nullptr) {
664 currentCount = OSNumber::withNumber(1, 64);
665 } else {
666 currentCount->addValue(1);
667 }
668
669 existingEntry->setObject(opCountKey, currentCount);
670 }
671
672 _stats->setObject(entryKey, existingEntry);
673 NVRAMUNLOCK();
674
675 exit:
676 IODeleteData(entryKey, char, entryKeySize);
677
678 return;
679 }
680
681 bool
serializeStats(void *,OSSerialize * serializer)682 IODTNVRAMDiags::serializeStats(void *, OSSerialize * serializer)
683 {
684 bool ok;
685
686 NVRAMREADLOCK();
687 ok = _stats->serialize(serializer);
688 NVRAMUNLOCK();
689
690 return ok;
691 }
692
693 // ************************** IODTNVRAMVariables ****************************
694
695 // private IOService based class for publishing distinct dictionary properties on
696 // for easy ioreg access since the serializeProperties call is overloaded and is used
697 // as variable access
698 class IODTNVRAMVariables : public IOService
699 {
700 OSDeclareDefaultStructors(IODTNVRAMVariables)
701 private:
702 IODTNVRAM *_provider;
703 uuid_t _guid;
704
705 public:
706 bool init(const uuid_t *guid);
707 virtual bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
708
709 virtual bool serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
710 virtual OSPtr<OSObject> copyProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
711 virtual OSObject *getProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
712 virtual bool setProperty(const OSSymbol *aKey, OSObject *anObject) APPLE_KEXT_OVERRIDE;
713 virtual IOReturn setProperties(OSObject *properties) APPLE_KEXT_OVERRIDE;
714 virtual void removeProperty(const OSSymbol *aKey) APPLE_KEXT_OVERRIDE;
715 };
716
OSDefineMetaClassAndStructors(IODTNVRAMVariables,IOService)717 OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
718
719 bool
720 IODTNVRAMVariables::init(const uuid_t *guid)
721 {
722 require(super::init() && (guid != nullptr), fail);
723
724 uuid_copy(_guid, *guid);
725
726 return true;
727
728 fail:
729 return false;
730 }
731
732 bool
start(IOService * provider)733 IODTNVRAMVariables::start(IOService * provider)
734 {
735 if (!super::start(provider)) {
736 goto error;
737 }
738
739 _provider = OSDynamicCast(IODTNVRAM, provider);
740 if (_provider == nullptr) {
741 goto error;
742 }
743
744 registerService();
745
746 return true;
747
748 error:
749 stop(provider);
750
751 return false;
752 }
753
754 bool
serializeProperties(OSSerialize * s) const755 IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
756 {
757 const OSSymbol *key;
758 OSSharedPtr<OSDictionary> dict;
759 OSSharedPtr<OSCollectionIterator> iter;
760 OSSharedPtr<OSDictionary> localVariables;
761 bool ok = false;
762 bool systemGuid = uuid_compare(_guid, gAppleSystemVariableGuid) == 0;
763
764 if (systemGuid) {
765 localVariables = _provider->_systemDict;
766 } else {
767 localVariables = _provider->_commonDict;
768 }
769
770 if (localVariables == nullptr) {
771 goto exit;
772 }
773
774 dict = OSDictionary::withCapacity(localVariables->getCount());
775 if (dict == nullptr) {
776 DEBUG_ERROR("No dictionary\n");
777 goto exit;
778 }
779
780 iter = OSCollectionIterator::withCollection(localVariables.get());
781 if (iter == nullptr) {
782 DEBUG_ERROR("failed to create iterator\n");
783 goto exit;
784 }
785
786 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
787 if (verifyPermission(kIONVRAMOperationRead, &_guid, key)) {
788 dict->setObject(key, localVariables->getObject(key));
789 }
790 }
791
792 ok = dict->serialize(s);
793
794 exit:
795 DEBUG_INFO("ok=%d\n", ok);
796 return ok;
797 }
798
799 OSPtr<OSObject>
copyProperty(const OSSymbol * aKey) const800 IODTNVRAMVariables::copyProperty(const OSSymbol *aKey) const
801 {
802 if (_provider && !skipKey(aKey)) {
803 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
804
805 return _provider->copyPropertyWithGUIDAndName(&_guid, aKey->getCStringNoCopy());
806 } else {
807 return nullptr;
808 }
809 }
810
811 OSObject *
getProperty(const OSSymbol * aKey) const812 IODTNVRAMVariables::getProperty(const OSSymbol *aKey) const
813 {
814 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
815
816 return theObject.get();
817 }
818
819 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)820 IODTNVRAMVariables::setProperty(const OSSymbol *aKey, OSObject *anObject)
821 {
822 if (_provider) {
823 return _provider->setPropertyWithGUIDAndName(&_guid, aKey->getCStringNoCopy(), anObject) == kIOReturnSuccess;
824 } else {
825 return false;
826 }
827 }
828
829 IOReturn
setProperties(OSObject * properties)830 IODTNVRAMVariables::setProperties(OSObject *properties)
831 {
832 IOReturn ret = kIOReturnSuccess;
833 OSObject *object;
834 const OSSymbol *key;
835 OSDictionary *dict;
836 OSSharedPtr<OSCollectionIterator> iter;
837
838 if (_provider) {
839 dict = OSDynamicCast(OSDictionary, properties);
840 if (dict == nullptr) {
841 DEBUG_ERROR("Not a dictionary\n");
842 return kIOReturnBadArgument;
843 }
844
845 iter = OSCollectionIterator::withCollection(dict);
846 if (iter == nullptr) {
847 DEBUG_ERROR("Couldn't create iterator\n");
848 return kIOReturnBadArgument;
849 }
850
851 while (ret == kIOReturnSuccess) {
852 key = OSDynamicCast(OSSymbol, iter->getNextObject());
853 if (key == nullptr) {
854 break;
855 }
856
857 object = dict->getObject(key);
858 if (object == nullptr) {
859 continue;
860 }
861
862 ret = setProperty(key, object);
863 }
864 } else {
865 ret = kIOReturnNotReady;
866 }
867
868 DEBUG_INFO("ret=%#08x\n", ret);
869
870 return ret;
871 }
872
873 void
removeProperty(const OSSymbol * aKey)874 IODTNVRAMVariables::removeProperty(const OSSymbol *aKey)
875 {
876 if (_provider) {
877 _provider->removePropertyWithGUIDAndName(&_guid, aKey->getCStringNoCopy());
878 }
879 }
880
881 // ************************** Format Handlers ***************************
882
~IODTNVRAMFormatHandler()883 IODTNVRAMFormatHandler::~IODTNVRAMFormatHandler()
884 {
885 }
886
887 #include "IONVRAMCHRPHandler.cpp"
888
889 #include "IONVRAMV3Handler.cpp"
890
891 // **************************** IODTNVRAM *********************************
892
893 bool
init(IORegistryEntry * old,const IORegistryPlane * plane)894 IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
895 {
896 OSSharedPtr<OSDictionary> dict;
897
898 DEBUG_INFO("...\n");
899
900 require(super::init(old, plane), fail);
901
902 #if XNU_TARGET_OS_OSX
903 #if CONFIG_CSR
904 gInternalBuild = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0);
905 DEBUG_INFO("gInternalBuild = %d\n", gInternalBuild);
906 #endif // CONFIG_CSR
907 #endif // XNU_TARGET_OS_OSX
908
909 _variableLock = IORWLockAlloc();
910 require(_variableLock != nullptr, fail);
911
912 _controllerLock = IOLockAlloc();
913 require(_controllerLock != nullptr, fail);
914
915 // Clear the IORegistryEntry property table
916 dict = OSDictionary::withCapacity(1);
917 require(dict != nullptr, fail);
918
919 setPropertyTable(dict.get());
920 dict.reset();
921
922 return true;
923
924 fail:
925 return false;
926 }
927
928 bool
start(IOService * provider)929 IODTNVRAM::start(IOService *provider)
930 {
931 OSSharedPtr<OSNumber> version;
932
933 DEBUG_INFO("...\n");
934
935 require(super::start(provider), fail);
936
937 // Check if our overridden init function was called
938 // If not, skip any additional initialization being done here.
939 // This is not an error we just need to successfully exit this function to allow
940 // AppleEFIRuntime to proceed and take over operation
941 require_action(_controllerLock != nullptr, no_common, DEBUG_INFO("x86 init\n"));
942
943 _diags = new IODTNVRAMDiags;
944 if (!_diags || !_diags->init()) {
945 DEBUG_ERROR("Unable to create/init the diags service\n");
946 OSSafeReleaseNULL(_diags);
947 goto fail;
948 }
949
950 if (!_diags->attach(this)) {
951 DEBUG_ERROR("Unable to attach the diags service!\n");
952 OSSafeReleaseNULL(_diags);
953 goto fail;
954 }
955
956 if (!_diags->start(this)) {
957 DEBUG_ERROR("Unable to start the diags service!\n");
958 _diags->detach(this);
959 OSSafeReleaseNULL(_diags);
960 goto fail;
961 }
962
963 // This will load the proxied variable data which will call back into
964 // IODTNVRAM for the variable sets which will also update the system/common services
965 initImageFormat();
966
967 version = OSNumber::withNumber(_format->getVersion(), 32);
968 _diags->setProperty(kCurrentNVRAMVersionKey, version.get());
969
970 if (_format->getSystemUsed()) {
971 _systemService = new IODTNVRAMVariables;
972
973 if (!_systemService || !_systemService->init(&gAppleSystemVariableGuid)) {
974 DEBUG_ERROR("Unable to start the system service!\n");
975 OSSafeReleaseNULL(_systemService);
976 goto no_system;
977 }
978
979 _systemService->setName("options-system");
980
981 if (!_systemService->attach(this)) {
982 DEBUG_ERROR("Unable to attach the system service!\n");
983 OSSafeReleaseNULL(_systemService);
984 goto no_system;
985 }
986
987 if (!_systemService->start(this)) {
988 DEBUG_ERROR("Unable to start the system service!\n");
989 _systemService->detach(this);
990 OSSafeReleaseNULL(_systemService);
991 goto no_system;
992 }
993 }
994
995 no_system:
996 _commonService = new IODTNVRAMVariables;
997
998 if (!_commonService || !_commonService->init(&gAppleNVRAMGuid)) {
999 DEBUG_ERROR("Unable to start the common service!\n");
1000 OSSafeReleaseNULL(_commonService);
1001 goto no_common;
1002 }
1003
1004 _commonService->setName("options-common");
1005
1006 if (!_commonService->attach(this)) {
1007 DEBUG_ERROR("Unable to attach the common service!\n");
1008 OSSafeReleaseNULL(_commonService);
1009 goto no_common;
1010 }
1011
1012 if (!_commonService->start(this)) {
1013 DEBUG_ERROR("Unable to start the common service!\n");
1014 _commonService->detach(this);
1015 OSSafeReleaseNULL(_commonService);
1016 goto no_common;
1017 }
1018
1019 no_common:
1020 return true;
1021
1022 fail:
1023 stop(provider);
1024 return false;
1025 }
1026
1027 void
initImageFormat(void)1028 IODTNVRAM::initImageFormat(void)
1029 {
1030 OSSharedPtr<IORegistryEntry> entry;
1031 OSSharedPtr<OSObject> prop;
1032 const char *proxyDataKey = "nvram-proxy-data";
1033 const char *bankSizeKey = "nvram-bank-size";
1034 OSData *data;
1035 uint32_t size = 0;
1036
1037 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1038
1039 require(entry != nullptr, skip);
1040
1041 prop = entry->copyProperty(bankSizeKey);
1042 require(prop != nullptr, skip);
1043
1044 data = OSDynamicCast(OSData, prop.get());
1045 require(data != nullptr, skip);
1046
1047 size = *((uint32_t*)data->getBytesNoCopy());
1048 DEBUG_ALWAYS("NVRAM size is %u bytes\n", size);
1049
1050 prop = entry->copyProperty(proxyDataKey);
1051 require(prop != nullptr, skip);
1052
1053 data = OSDynamicCast(OSData, prop.get());
1054 require(data != nullptr, skip);
1055
1056 if (IONVRAMV3Handler::isValidImage((const uint8_t *)data->getBytesNoCopy(), size)) {
1057 _format = IONVRAMV3Handler::init(this, (const uint8_t *)data->getBytesNoCopy(), size, _commonDict, _systemDict);
1058 require_action(_format, skip, panic("IONVRAMV3Handler creation failed\n"));
1059 } else {
1060 _format = IONVRAMCHRPHandler::init(this, (const uint8_t *)data->getBytesNoCopy(), size, _commonDict, _systemDict);
1061 require_action(_format, skip, panic("IONVRAMCHRPHandler creation failed\n"));
1062 }
1063
1064 #if defined(RELEASE)
1065 if (entry != nullptr) {
1066 entry->removeProperty(proxyDataKey);
1067 }
1068 #endif
1069
1070 skip:
1071 _lastDeviceSync = 0;
1072 _freshInterval = true;
1073 }
1074
1075 void
registerNVRAMController(IONVRAMController * controller)1076 IODTNVRAM::registerNVRAMController(IONVRAMController *controller)
1077 {
1078 DEBUG_INFO("setting controller\n");
1079
1080 NVRAMWRITELOCK();
1081 CONTROLLERLOCK();
1082
1083 _format->setController(controller);
1084
1085 CONTROLLERUNLOCK();
1086 NVRAMUNLOCK();
1087
1088 return;
1089 }
1090
1091 bool
safeToSync(void)1092 IODTNVRAM::safeToSync(void)
1093 {
1094 AbsoluteTime delta;
1095 UInt64 delta_ns;
1096 SInt32 delta_secs;
1097
1098 // delta interval went by
1099 clock_get_uptime(&delta);
1100
1101 // Figure it in seconds.
1102 absolutetime_to_nanoseconds(delta, &delta_ns);
1103 delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
1104
1105 if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
1106 _lastDeviceSync = delta_secs;
1107 _freshInterval = false;
1108 return true;
1109 }
1110
1111 return false;
1112 }
1113
1114 void
syncInternal(bool rateLimit)1115 IODTNVRAM::syncInternal(bool rateLimit)
1116 {
1117 DEBUG_INFO("rateLimit=%d\n", rateLimit);
1118
1119 if (!SAFE_TO_LOCK()) {
1120 DEBUG_INFO("cannot lock\n");
1121 return;
1122 }
1123
1124 // Rate limit requests to sync. Drivers that need this rate limiting will
1125 // shadow the data and only write to flash when they get a sync call
1126 if (rateLimit) {
1127 if (safeToSync() == false) {
1128 DEBUG_INFO("safeToSync()=false\n");
1129 return;
1130 }
1131 }
1132
1133 DEBUG_INFO("Calling sync()\n");
1134
1135 NVRAMREADLOCK();
1136 CONTROLLERLOCK();
1137
1138 _format->sync();
1139
1140 CONTROLLERUNLOCK();
1141 NVRAMUNLOCK();
1142
1143 if (_diags) {
1144 OSSharedPtr<OSNumber> generation = OSNumber::withNumber(_format->getGeneration(), 32);
1145 _diags->setProperty(kCurrentGenerationCountKey, generation.get());
1146 }
1147 }
1148
1149 void
sync(void)1150 IODTNVRAM::sync(void)
1151 {
1152 syncInternal(false);
1153 }
1154
1155 bool
serializeProperties(OSSerialize * s) const1156 IODTNVRAM::serializeProperties(OSSerialize *s) const
1157 {
1158 const OSSymbol *key;
1159 OSSharedPtr<OSDictionary> systemDict, commonDict, dict;
1160 OSSharedPtr<OSCollectionIterator> iter;
1161 bool ok = false;
1162 unsigned int totalCapacity = 0;
1163
1164 NVRAMREADLOCK();
1165 if (_commonDict) {
1166 commonDict = OSDictionary::withDictionary(_commonDict.get());
1167 }
1168
1169 if (_systemDict) {
1170 systemDict = OSDictionary::withDictionary(_systemDict.get());
1171 }
1172 NVRAMUNLOCK();
1173
1174 totalCapacity += (commonDict != nullptr) ? commonDict->getCapacity() : 0;
1175 totalCapacity += (systemDict != nullptr) ? systemDict->getCapacity() : 0;
1176
1177 dict = OSDictionary::withCapacity(totalCapacity);
1178
1179 if (dict == nullptr) {
1180 DEBUG_ERROR("No dictionary\n");
1181 goto exit;
1182 }
1183
1184 // Copy system entries first if present then copy unique common entries
1185 if (systemDict != nullptr) {
1186 iter = OSCollectionIterator::withCollection(systemDict.get());
1187 if (iter == nullptr) {
1188 DEBUG_ERROR("failed to create iterator\n");
1189 goto exit;
1190 }
1191
1192 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1193 if (verifyPermission(kIONVRAMOperationRead, &gAppleSystemVariableGuid, key)) {
1194 dict->setObject(key, systemDict->getObject(key));
1195 }
1196 }
1197
1198 iter.reset();
1199 }
1200
1201 if (commonDict != nullptr) {
1202 iter = OSCollectionIterator::withCollection(commonDict.get());
1203 if (iter == nullptr) {
1204 DEBUG_ERROR("failed to create common iterator\n");
1205 goto exit;
1206 }
1207
1208 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1209 if (dict->getObject(key) != nullptr) {
1210 // Skip non uniques
1211 continue;
1212 }
1213 if (verifyPermission(kIONVRAMOperationRead, &gAppleNVRAMGuid, key)) {
1214 dict->setObject(key, commonDict->getObject(key));
1215 }
1216 }
1217 }
1218
1219 ok = dict->serialize(s);
1220
1221 exit:
1222 DEBUG_INFO("ok=%d\n", ok);
1223
1224 return ok;
1225 }
1226
1227 NVRAMPartitionType
getDictionaryType(const OSDictionary * dict) const1228 IODTNVRAM::getDictionaryType(const OSDictionary *dict) const
1229 {
1230 if (dict == _commonDict) {
1231 return kIONVRAMPartitionCommon;
1232 } else if (dict == _systemDict) {
1233 return kIONVRAMPartitionSystem;
1234 } else {
1235 return kIONVRAMPartitionTypeUnknown;
1236 }
1237 }
1238
1239 IOReturn
chooseDictionary(IONVRAMOperation operation,const uuid_t * varGuid,const char * variableName,OSDictionary ** dict) const1240 IODTNVRAM::chooseDictionary(IONVRAMOperation operation, const uuid_t *varGuid, const char *variableName, OSDictionary **dict) const
1241 {
1242 if (_systemDict != nullptr) {
1243 bool systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0;
1244
1245 if (variableInAllowList(variableName)) {
1246 DEBUG_INFO("Using system dictionary due to allow list\n");
1247 if (!systemGuid) {
1248 DEBUG_ERROR("System GUID NOT used for %s\n", variableName);
1249 }
1250 *dict = _systemDict.get();
1251 } else if (systemGuid) {
1252 DEBUG_INFO("Using system dictionary via GUID\n");
1253 *dict = _systemDict.get();
1254 } else {
1255 DEBUG_INFO("Using common dictionary\n");
1256 *dict = _commonDict.get();
1257 }
1258 return kIOReturnSuccess;
1259 } else if (_commonDict != nullptr) {
1260 DEBUG_INFO("Defaulting to common dictionary\n");
1261 *dict = _commonDict.get();
1262 return kIOReturnSuccess;
1263 }
1264
1265 return kIOReturnNotFound;
1266 }
1267
1268 IOReturn
flushDict(const uuid_t * guid,IONVRAMOperation op)1269 IODTNVRAM::flushDict(const uuid_t *guid, IONVRAMOperation op)
1270 {
1271 IOReturn err = kIOReturnSuccess;
1272
1273 if ((_systemDict != nullptr) && (uuid_compare(*guid, gAppleSystemVariableGuid) == 0)) {
1274 const OSSymbol *key;
1275 OSSharedPtr<OSDictionary> newDict;
1276 OSSharedPtr<OSCollectionIterator> iter;
1277
1278 newDict = OSDictionary::withDictionary(_systemDict.get());
1279 iter = OSCollectionIterator::withCollection(newDict.get());
1280 if ((newDict == nullptr) || (iter == nullptr)) {
1281 err = kIOReturnNoMemory;
1282 goto exit;
1283 }
1284
1285 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1286 if (verifyPermission(op, &gAppleSystemVariableGuid, key)) {
1287 _format->setVariable(guid, key->getCStringNoCopy(), nullptr);
1288 }
1289 }
1290
1291 DEBUG_INFO("system dictionary flushed\n");
1292 } else if ((_commonDict != nullptr) && (uuid_compare(*guid, gAppleNVRAMGuid) == 0)) {
1293 const OSSymbol *key;
1294 OSSharedPtr<OSDictionary> newDict;
1295 OSSharedPtr<OSCollectionIterator> iter;
1296
1297 newDict = OSDictionary::withDictionary(_commonDict.get());
1298 iter = OSCollectionIterator::withCollection(newDict.get());
1299 if ((newDict == nullptr) || (iter == nullptr)) {
1300 err = kIOReturnNoMemory;
1301 goto exit;
1302 }
1303
1304 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1305 if (verifyPermission(op, &gAppleNVRAMGuid, key)) {
1306 _format->setVariable(guid, key->getCStringNoCopy(), nullptr);
1307 }
1308 }
1309
1310 DEBUG_INFO("common dictionary flushed\n");
1311 }
1312
1313 exit:
1314 return err;
1315 }
1316
1317 bool
handleSpecialVariables(const char * name,const uuid_t * guid,const OSObject * obj,IOReturn * error)1318 IODTNVRAM::handleSpecialVariables(const char *name, const uuid_t *guid, const OSObject *obj, IOReturn *error)
1319 {
1320 IOReturn err = kIOReturnSuccess;
1321 bool special = false;
1322
1323 NVRAMLOCKASSERTEXCLUSIVE();
1324
1325 // ResetNVRam flushes both regions in one call
1326 // Obliterate can flush either separately
1327 if (strcmp(name, "ObliterateNVRam") == 0) {
1328 special = true;
1329 err = flushDict(guid, kIONVRAMOperationObliterate);
1330 } else if (strcmp(name, "ResetNVRam") == 0) {
1331 special = true;
1332 err = flushDict(&gAppleSystemVariableGuid, kIONVRAMOperationReset);
1333
1334 if (err != kIOReturnSuccess) {
1335 goto exit;
1336 }
1337
1338 err = flushDict(&gAppleNVRAMGuid, kIONVRAMOperationReset);
1339 }
1340
1341 exit:
1342 if (error) {
1343 *error = err;
1344 }
1345
1346 return special;
1347 }
1348
1349 OSSharedPtr<OSObject>
copyPropertyWithGUIDAndName(const uuid_t * guid,const char * name) const1350 IODTNVRAM::copyPropertyWithGUIDAndName(const uuid_t *guid, const char *name) const
1351 {
1352 IOReturn result;
1353 OSDictionary *dict;
1354 OSSharedPtr<OSObject> theObject = nullptr;
1355
1356 result = chooseDictionary(kIONVRAMOperationRead, guid, name, &dict);
1357 if (result != kIOReturnSuccess) {
1358 DEBUG_INFO("No dictionary\n");
1359 goto exit;
1360 }
1361
1362 if (!verifyPermission(kIONVRAMOperationRead, guid, name)) {
1363 DEBUG_INFO("Not privileged\n");
1364 goto exit;
1365 }
1366
1367 NVRAMREADLOCK();
1368 theObject.reset(dict->getObject(name), OSRetain);
1369 NVRAMUNLOCK();
1370
1371 if (_diags) {
1372 _diags->logVariable(getDictionaryType(dict), kIONVRAMOperationRead, name, NULL);
1373 }
1374
1375 if (theObject != nullptr) {
1376 DEBUG_INFO("found data\n");
1377 }
1378
1379 exit:
1380 return theObject;
1381 }
1382
1383 OSSharedPtr<OSObject>
copyProperty(const OSSymbol * aKey) const1384 IODTNVRAM::copyProperty(const OSSymbol *aKey) const
1385 {
1386 const char *variableName;
1387 uuid_t varGuid;
1388
1389 if (skipKey(aKey)) {
1390 return nullptr;
1391 }
1392 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1393
1394 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1395
1396 return copyPropertyWithGUIDAndName(&varGuid, variableName);
1397 }
1398
1399 OSSharedPtr<OSObject>
copyProperty(const char * aKey) const1400 IODTNVRAM::copyProperty(const char *aKey) const
1401 {
1402 OSSharedPtr<const OSSymbol> keySymbol;
1403 OSSharedPtr<OSObject> theObject;
1404
1405 keySymbol = OSSymbol::withCString(aKey);
1406 if (keySymbol != nullptr) {
1407 theObject = copyProperty(keySymbol.get());
1408 }
1409
1410 return theObject;
1411 }
1412
1413 OSObject *
getProperty(const OSSymbol * aKey) const1414 IODTNVRAM::getProperty(const OSSymbol *aKey) const
1415 {
1416 // The shared pointer gets released at the end of the function,
1417 // and returns a view into theObject.
1418 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1419
1420 return theObject.get();
1421 }
1422
1423 OSObject *
getProperty(const char * aKey) const1424 IODTNVRAM::getProperty(const char *aKey) const
1425 {
1426 // The shared pointer gets released at the end of the function,
1427 // and returns a view into theObject.
1428 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1429
1430 return theObject.get();
1431 }
1432
1433 IOReturn
setPropertyWithGUIDAndName(const uuid_t * guid,const char * name,OSObject * anObject)1434 IODTNVRAM::setPropertyWithGUIDAndName(const uuid_t *guid, const char *name, OSObject *anObject)
1435 {
1436 IOReturn ret = kIOReturnSuccess;
1437 bool remove = false;
1438 OSString *tmpString = nullptr;
1439 OSSharedPtr<OSObject> propObject;
1440 OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
1441 bool deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
1442 bool ok;
1443 size_t propDataSize = 0;
1444
1445 deletePropertyKey = strncmp(name, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
1446 syncNowPropertyKey = strncmp(name, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
1447 forceSyncNowPropertyKey = strncmp(name, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
1448
1449 if (deletePropertyKey) {
1450 tmpString = OSDynamicCast(OSString, anObject);
1451 if (tmpString != nullptr) {
1452 const char *variableName;
1453 uuid_t varGuid;
1454 IOReturn removeRet;
1455
1456 parseVariableName(tmpString->getCStringNoCopy(), &varGuid, &variableName);
1457 removeRet = removePropertyWithGUIDAndName(&varGuid, variableName);
1458
1459 DEBUG_INFO("kIONVRAMDeletePropertyKey found, removeRet=%#08x\n", removeRet);
1460 } else {
1461 DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1462 ret = kIOReturnError;
1463 }
1464 goto exit;
1465 } else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
1466 tmpString = OSDynamicCast(OSString, anObject);
1467 DEBUG_INFO("NVRAM sync key %s found\n", name);
1468 if (tmpString != nullptr) {
1469 // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1470 syncInternal(syncNowPropertyKey);
1471 } else {
1472 DEBUG_INFO("%s value needs to be an OSString\n", name);
1473 ret = kIOReturnError;
1474 }
1475 goto exit;
1476 }
1477
1478 if (!verifyPermission(kIONVRAMOperationWrite, guid, name)) {
1479 DEBUG_INFO("Not privileged\n");
1480 ret = kIOReturnNotPrivileged;
1481 goto exit;
1482 }
1483
1484 // Make sure the object is of the correct type.
1485 switch (getVariableType(name)) {
1486 case kOFVariableTypeBoolean:
1487 propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
1488 break;
1489
1490 case kOFVariableTypeNumber:
1491 propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
1492 break;
1493
1494 case kOFVariableTypeString:
1495 propObject = OSDynamicPtrCast<OSString>(sharedObject);
1496 if (propObject != nullptr) {
1497 propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
1498
1499 if ((strncmp(name, kIONVRAMBootArgsKey, sizeof(kIONVRAMBootArgsKey)) == 0) && (propDataSize >= BOOT_LINE_LENGTH)) {
1500 DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
1501 ret = kIOReturnNoSpace;
1502 goto exit;
1503 }
1504 }
1505 break;
1506
1507 case kOFVariableTypeData:
1508 propObject = OSDynamicPtrCast<OSData>(sharedObject);
1509 if (propObject == nullptr) {
1510 tmpString = OSDynamicCast(OSString, sharedObject.get());
1511 if (tmpString != nullptr) {
1512 propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
1513 tmpString->getLength());
1514 }
1515 }
1516
1517 if (propObject != nullptr) {
1518 propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
1519 }
1520
1521 #if defined(XNU_TARGET_OS_OSX)
1522 if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
1523 remove = true;
1524 }
1525 #endif /* defined(XNU_TARGET_OS_OSX) */
1526 break;
1527 default:
1528 break;
1529 }
1530
1531 if (propObject == nullptr) {
1532 DEBUG_INFO("No property object\n");
1533 ret = kIOReturnBadArgument;
1534 goto exit;
1535 }
1536
1537 if (!verifyWriteSizeLimit(guid, name, propDataSize)) {
1538 DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, name);
1539 ret = kIOReturnNoSpace;
1540 goto exit;
1541 }
1542
1543 NVRAMWRITELOCK();
1544 ok = handleSpecialVariables(name, guid, propObject.get(), &ret);
1545 NVRAMUNLOCK();
1546
1547 if (ok) {
1548 goto exit;
1549 }
1550
1551 if (remove == false) {
1552 DEBUG_INFO("Adding object\n");
1553 NVRAMWRITELOCK();
1554 ret = _format->setVariable(guid, name, propObject.get());
1555 NVRAMUNLOCK();
1556 } else {
1557 DEBUG_INFO("Removing object\n");
1558 ret = removePropertyWithGUIDAndName(guid, name);
1559 }
1560
1561 if (tmpString) {
1562 propObject.reset();
1563 }
1564
1565 exit:
1566 DEBUG_INFO("ret=%#08x\n", ret);
1567
1568 return ret;
1569 }
1570
1571 IOReturn
setPropertyInternal(const OSSymbol * aKey,OSObject * anObject)1572 IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
1573 {
1574 const char *variableName;
1575 uuid_t varGuid;
1576
1577 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1578
1579 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1580
1581 return setPropertyWithGUIDAndName(&varGuid, variableName, anObject);
1582 }
1583
1584 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)1585 IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
1586 {
1587 return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
1588 }
1589
1590 void
removeProperty(const OSSymbol * aKey)1591 IODTNVRAM::removeProperty(const OSSymbol *aKey)
1592 {
1593 IOReturn ret;
1594
1595 ret = removePropertyInternal(aKey);
1596
1597 if (ret != kIOReturnSuccess) {
1598 DEBUG_INFO("removePropertyInternal failed, ret=%#08x\n", ret);
1599 }
1600 }
1601
1602 IOReturn
removePropertyWithGUIDAndName(const uuid_t * guid,const char * name)1603 IODTNVRAM::removePropertyWithGUIDAndName(const uuid_t *guid, const char *name)
1604 {
1605 IOReturn ret;
1606 OSDictionary *dict;
1607
1608 DEBUG_INFO("name=%s\n", name);
1609
1610 ret = chooseDictionary(kIONVRAMOperationDelete, guid, name, &dict);
1611 if (ret != kIOReturnSuccess) {
1612 DEBUG_INFO("No dictionary\n");
1613 goto exit;
1614 }
1615
1616 if (!verifyPermission(kIONVRAMOperationDelete, guid, name)) {
1617 DEBUG_INFO("Not priveleged\n");
1618 ret = kIOReturnNotPrivileged;
1619 goto exit;
1620 }
1621
1622 NVRAMWRITELOCK();
1623
1624 if (_format->setVariable(guid, name, nullptr) != kIOReturnSuccess) {
1625 DEBUG_INFO("%s not found\n", name);
1626 ret = kIOReturnNotFound;
1627 }
1628
1629 NVRAMUNLOCK();
1630
1631 exit:
1632 return ret;
1633 }
1634
1635 IOReturn
removePropertyInternal(const OSSymbol * aKey)1636 IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
1637 {
1638 IOReturn ret;
1639 const char *variableName;
1640 uuid_t varGuid;
1641
1642 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1643
1644 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1645
1646 ret = removePropertyWithGUIDAndName(&varGuid, variableName);
1647
1648 return ret;
1649 }
1650
1651 IOReturn
setProperties(OSObject * properties)1652 IODTNVRAM::setProperties(OSObject *properties)
1653 {
1654 IOReturn ret = kIOReturnSuccess;
1655 OSObject *object;
1656 const OSSymbol *key;
1657 OSDictionary *dict;
1658 OSSharedPtr<OSCollectionIterator> iter;
1659
1660 dict = OSDynamicCast(OSDictionary, properties);
1661 if (dict == nullptr) {
1662 DEBUG_ERROR("Not a dictionary\n");
1663 return kIOReturnBadArgument;
1664 }
1665
1666 iter = OSCollectionIterator::withCollection(dict);
1667 if (iter == nullptr) {
1668 DEBUG_ERROR("Couldn't create iterator\n");
1669 return kIOReturnBadArgument;
1670 }
1671
1672 while (ret == kIOReturnSuccess) {
1673 key = OSDynamicCast(OSSymbol, iter->getNextObject());
1674 if (key == nullptr) {
1675 break;
1676 }
1677
1678 object = dict->getObject(key);
1679 if (object == nullptr) {
1680 continue;
1681 }
1682
1683 ret = setPropertyInternal(key, object);
1684 }
1685
1686 DEBUG_INFO("ret=%#08x\n", ret);
1687
1688 return ret;
1689 }
1690
1691 // ********************** Deprecated ********************
1692
1693 IOReturn
readXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)1694 IODTNVRAM::readXPRAM(IOByteCount offset, uint8_t *buffer,
1695 IOByteCount length)
1696 {
1697 return kIOReturnUnsupported;
1698 }
1699
1700 IOReturn
writeXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)1701 IODTNVRAM::writeXPRAM(IOByteCount offset, uint8_t *buffer,
1702 IOByteCount length)
1703 {
1704 return kIOReturnUnsupported;
1705 }
1706
1707 IOReturn
readNVRAMProperty(IORegistryEntry * entry,const OSSymbol ** name,OSData ** value)1708 IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
1709 const OSSymbol **name,
1710 OSData **value)
1711 {
1712 return kIOReturnUnsupported;
1713 }
1714
1715 IOReturn
writeNVRAMProperty(IORegistryEntry * entry,const OSSymbol * name,OSData * value)1716 IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
1717 const OSSymbol *name,
1718 OSData *value)
1719 {
1720 return kIOReturnUnsupported;
1721 }
1722
1723 OSDictionary *
getNVRAMPartitions(void)1724 IODTNVRAM::getNVRAMPartitions(void)
1725 {
1726 return NULL;
1727 }
1728
1729 IOReturn
readNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)1730 IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
1731 IOByteCount offset, uint8_t *buffer,
1732 IOByteCount length)
1733 {
1734 return kIOReturnUnsupported;
1735 }
1736
1737 IOReturn
writeNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)1738 IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
1739 IOByteCount offset, uint8_t *buffer,
1740 IOByteCount length)
1741 {
1742 return kIOReturnUnsupported;
1743 }
1744
1745 IOByteCount
savePanicInfo(uint8_t * buffer,IOByteCount length)1746 IODTNVRAM::savePanicInfo(uint8_t *buffer, IOByteCount length)
1747 {
1748 return 0;
1749 }
1750