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, gAppleSystemVariableGuid) == 0) {
291 return kIONVRAMPartitionSystem;
292 } else {
293 return kIONVRAMPartitionCommon;
294 }
295 }
296
297 static IONVRAMVariableType
getVariableType(const char * propName)298 getVariableType(const char *propName)
299 {
300 const VariableTypeEntry *entry;
301
302 entry = gVariableTypes;
303 while (entry->name != nullptr) {
304 if (strcmp(entry->name, propName) == 0) {
305 break;
306 }
307 entry++;
308 }
309
310 return entry->type;
311 }
312
313 static IONVRAMVariableType
getVariableType(const OSSymbol * propSymbol)314 getVariableType(const OSSymbol *propSymbol)
315 {
316 return getVariableType(propSymbol->getCStringNoCopy());
317 }
318
319 static VariablePermission
getVariablePermission(const char * propName)320 getVariablePermission(const char *propName)
321 {
322 const VariablePermissionEntry *entry;
323
324 entry = gVariablePermissions;
325 while (entry->name != nullptr) {
326 if (strcmp(entry->name, propName) == 0) {
327 break;
328 }
329 entry++;
330 }
331
332 return entry->p;
333 }
334
335 static bool
variableInAllowList(const char * varName)336 variableInAllowList(const char *varName)
337 {
338 unsigned int i = 0;
339
340 while (gNVRAMSystemList[i] != nullptr) {
341 if (strcmp(varName, gNVRAMSystemList[i]) == 0) {
342 return true;
343 }
344 i++;
345 }
346
347 return false;
348 }
349
350 static bool
verifyWriteSizeLimit(const uuid_t varGuid,const char * variableName,size_t propDataSize)351 verifyWriteSizeLimit(const uuid_t varGuid, const char *variableName, size_t propDataSize)
352 {
353 if (variableInAllowList(variableName)) {
354 if (strnstr(variableName, "breadcrumbs", strlen(variableName)) != NULL) {
355 return propDataSize <= 1024;
356 } else {
357 return propDataSize <= 768;
358 }
359 }
360
361 return true;
362 }
363
364 #if defined(DEBUG) || defined(DEVELOPMENT)
365 static const char *
getNVRAMOpString(IONVRAMOperation op)366 getNVRAMOpString(IONVRAMOperation op)
367 {
368 switch (op) {
369 case kIONVRAMOperationRead:
370 return "Read";
371 case kIONVRAMOperationWrite:
372 return "Write";
373 case kIONVRAMOperationDelete:
374 return "Delete";
375 case kIONVRAMOperationObliterate:
376 return "Obliterate";
377 case kIONVRAMOperationReset:
378 return "Reset";
379 case kIONVRAMOperationInit:
380 return "Init";
381 default:
382 return "Unknown";
383 }
384 }
385 #endif
386
387 static bool
verifyPermission(IONVRAMOperation op,const uuid_t varGuid,const char * varName)388 verifyPermission(IONVRAMOperation op, const uuid_t varGuid, const char *varName)
389 {
390 VariablePermission perm;
391 bool kernel, writeEntitled = false, readEntitled = false, allowList, systemGuid = false, systemEntitled = false, systemInternalEntitled = false, systemAllow, systemReadHiddenAllow = false;
392 bool admin = false;
393 bool ok = false;
394
395 perm = getVariablePermission(varName);
396
397 kernel = current_task() == kernel_task;
398
399 if (perm.Bits.KernelOnly) {
400 DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
401 ok = kernel;
402 goto exit;
403 }
404
405 allowList = variableInAllowList(varName);
406 systemGuid = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
407 admin = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess;
408 writeEntitled = IOCurrentTaskHasEntitlement(kIONVRAMWriteAccessKey);
409 readEntitled = IOCurrentTaskHasEntitlement(kIONVRAMReadAccessKey);
410 systemEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemAllowKey);
411 systemInternalEntitled = IOCurrentTaskHasEntitlement(kIONVRAMSystemInternalAllowKey);
412 systemReadHiddenAllow = IOCurrentTaskHasEntitlement(kIONVRAMSystemHiddenAllowKey);
413
414 systemAllow = systemEntitled || (systemInternalEntitled && gInternalBuild) || kernel;
415
416 switch (op) {
417 case kIONVRAMOperationRead:
418 if (systemGuid && perm.Bits.SystemReadHidden) {
419 ok = systemReadHiddenAllow;
420 } else if (kernel || admin || readEntitled || perm.Bits.FullAccess) {
421 ok = true;
422 }
423 break;
424
425 case kIONVRAMOperationWrite:
426 if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
427 if (systemGuid) {
428 if (allowList) {
429 if (!systemAllow) {
430 DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
431 }
432 } else if (!systemAllow) {
433 DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
434 break;
435 }
436 }
437 ok = true;
438 }
439 break;
440
441 case kIONVRAMOperationDelete:
442 case kIONVRAMOperationObliterate:
443 case kIONVRAMOperationReset:
444 if (perm.Bits.NeverAllowedToDelete) {
445 DEBUG_INFO("Never allowed to delete %s\n", varName);
446 break;
447 } else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) {
448 DEBUG_INFO("Not allowed to obliterate %s\n", varName);
449 break;
450 } else if ((op == kIONVRAMOperationDelete) && perm.Bits.ResetNVRAMOnlyDelete) {
451 DEBUG_INFO("Only allowed to delete %s via NVRAM reset\n", varName);
452 break;
453 }
454
455 if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
456 if (systemGuid) {
457 if (allowList) {
458 if (!systemAllow) {
459 DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
460 }
461 } else if (!systemAllow) {
462 DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
463 break;
464 }
465 }
466 ok = true;
467 }
468 break;
469
470 case kIONVRAMOperationInit:
471 break;
472 }
473
474 exit:
475 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",
476 kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled, systemInternalEntitled, systemReadHiddenAllow, perm.Bits.UserWrite);
477
478 return ok;
479 }
480
481 static bool
verifyPermission(IONVRAMOperation op,const uuid_t varGuid,const OSSymbol * varName)482 verifyPermission(IONVRAMOperation op, const uuid_t varGuid, const OSSymbol *varName)
483 {
484 return verifyPermission(op, varGuid, varName->getCStringNoCopy());
485 }
486
487 /*
488 * Parse a variable name of the form "GUID:name".
489 * If the name cannot be parsed, substitute the Apple global variable GUID.
490 * Returns TRUE if a GUID was found in the name, FALSE otherwise.
491 * The guidResult and nameResult arguments may be nullptr if you just want
492 * to check the format of the string.
493 */
494 static bool
parseVariableName(const char * key,uuid_t * guidResult,const char ** nameResult)495 parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult)
496 {
497 uuid_string_t temp = {0};
498 size_t keyLen = strlen(key);
499 bool ok = false;
500 const char *name = key;
501 uuid_t guid;
502
503 if (keyLen > sizeof(temp)) {
504 // check for at least UUID + ":" + more
505 memcpy(temp, key, sizeof(temp) - 1);
506
507 if ((uuid_parse(temp, guid) == 0) &&
508 (key[sizeof(temp) - 1] == ':')) {
509 name = key + sizeof(temp);
510 ok = true;
511 }
512 }
513
514 if (guidResult) {
515 ok ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
516 }
517 if (nameResult) {
518 *nameResult = name;
519 }
520
521 return ok;
522 }
523
524 static bool
skipKey(const OSSymbol * aKey)525 skipKey(const OSSymbol *aKey)
526 {
527 return aKey->isEqualTo(kIOClassNameOverrideKey) ||
528 aKey->isEqualTo(kIOBSDNameKey) ||
529 aKey->isEqualTo(kIOBSDNamesKey) ||
530 aKey->isEqualTo(kIOBSDMajorKey) ||
531 aKey->isEqualTo(kIOBSDMinorKey) ||
532 aKey->isEqualTo(kIOBSDUnitKey) ||
533 aKey->isEqualTo(kIOUserServicePropertiesKey) ||
534 aKey->isEqualTo(kIOMatchCategoryKey);
535 }
536
537
538 // ************************** IODTNVRAMDiags ****************************
539
540 #define kIODTNVRAMDiagsStatsKey "Stats"
541 #define kIODTNVRAMDiagsInitKey "Init"
542 #define kIODTNVRAMDiagsReadKey "Read"
543 #define kIODTNVRAMDiagsWriteKey "Write"
544 #define kIODTNVRAMDiagsDeleteKey "Delete"
545 #define kIODTNVRAMDiagsNameKey "Name"
546 #define kIODTNVRAMDiagsSizeKey "Size"
547 #define kIODTNVRAMDiagsPresentKey "Present"
548
549 // private IOService based class for publishing diagnostic info for IODTNVRAM
550 class IODTNVRAMDiags : public IOService
551 {
552 OSDeclareDefaultStructors(IODTNVRAMDiags)
553 private:
554 IODTNVRAM *_provider;
555 IORWLock *_variableLock;
556 OSSharedPtr<OSDictionary> _stats;
557
558 bool serializeStats(void *, OSSerialize * serializer);
559
560 public:
561 bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
562 void logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data);
563 };
564
OSDefineMetaClassAndStructors(IODTNVRAMDiags,IOService)565 OSDefineMetaClassAndStructors(IODTNVRAMDiags, IOService)
566
567 bool
568 IODTNVRAMDiags::start(IOService * provider)
569 {
570 OSSharedPtr<OSSerializer> serializer;
571
572 require(super::start(provider), error);
573
574 _variableLock = IORWLockAlloc();
575 require(_variableLock != nullptr, error);
576
577 _provider = OSDynamicCast(IODTNVRAM, provider);
578 require(_provider != nullptr, error);
579
580 _stats = OSDictionary::withCapacity(1);
581 require(_stats != nullptr, error);
582
583 serializer = OSSerializer::forTarget(this, OSMemberFunctionCast(OSSerializerCallback, this, &IODTNVRAMDiags::serializeStats));
584 require(serializer != nullptr, error);
585
586 setProperty(kIODTNVRAMDiagsStatsKey, serializer.get());
587
588 registerService();
589
590 return true;
591
592 error:
593 stop(provider);
594
595 return false;
596 }
597
598 void
logVariable(NVRAMPartitionType region,IONVRAMOperation op,const char * name,void * data)599 IODTNVRAMDiags::logVariable(NVRAMPartitionType region, IONVRAMOperation op, const char *name, void *data)
600 {
601 // "Stats" : OSDictionary
602 // - "XX:varName" : OSDictionary, XX is the region value prefix to distinguish which dictionary the variable is in
603 // - "Init" : OSBoolean True/present if variable present at initialization
604 // - "Read" : OSNumber count
605 // - "Write" : OSNumber count
606 // - "Delete" : OSNumber count
607 // - "Size" : OSNumber size, latest size from either init or write
608 // - "Present" : OSBoolean True/False if variable is present or not
609 char *entryKey;
610 size_t entryKeySize;
611 OSSharedPtr<OSDictionary> existingEntry;
612 OSSharedPtr<OSNumber> currentCount;
613 OSSharedPtr<OSNumber> varSize;
614 const char *opCountKey = nullptr;
615
616 entryKeySize = strlen("XX:") + strlen(name) + 1;
617 entryKey = IONewData(char, entryKeySize);
618 require(entryKey, exit);
619
620 snprintf(entryKey, entryKeySize, "%02X:%s", region, name);
621
622 NVRAMWRITELOCK();
623 existingEntry.reset(OSDynamicCast(OSDictionary, _stats->getObject(entryKey)), OSRetain);
624
625 if (existingEntry == nullptr) {
626 existingEntry = OSDictionary::withCapacity(4);
627 }
628
629 switch (op) {
630 case kIONVRAMOperationRead:
631 opCountKey = kIODTNVRAMDiagsReadKey;
632 if (existingEntry->getObject(kIODTNVRAMDiagsPresentKey) == nullptr) {
633 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
634 }
635 break;
636 case kIONVRAMOperationWrite:
637 opCountKey = kIODTNVRAMDiagsWriteKey;
638 varSize = OSNumber::withNumber((size_t)data, 64);
639 existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
640 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
641 break;
642 case kIONVRAMOperationDelete:
643 case kIONVRAMOperationObliterate:
644 case kIONVRAMOperationReset:
645 opCountKey = kIODTNVRAMDiagsDeleteKey;
646 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanFalse);
647 break;
648 case kIONVRAMOperationInit:
649 varSize = OSNumber::withNumber((size_t)data, 64);
650 existingEntry->setObject(kIODTNVRAMDiagsInitKey, varSize);
651 existingEntry->setObject(kIODTNVRAMDiagsSizeKey, varSize);
652 existingEntry->setObject(kIODTNVRAMDiagsPresentKey, kOSBooleanTrue);
653 break;
654 default:
655 goto exit;
656 }
657
658 if (opCountKey) {
659 currentCount.reset(OSDynamicCast(OSNumber, existingEntry->getObject(opCountKey)), OSRetain);
660
661 if (currentCount == nullptr) {
662 currentCount = OSNumber::withNumber(1, 64);
663 } else {
664 currentCount->addValue(1);
665 }
666
667 existingEntry->setObject(opCountKey, currentCount);
668 }
669
670 _stats->setObject(entryKey, existingEntry);
671 NVRAMUNLOCK();
672
673 exit:
674 IODeleteData(entryKey, char, entryKeySize);
675
676 return;
677 }
678
679 bool
serializeStats(void *,OSSerialize * serializer)680 IODTNVRAMDiags::serializeStats(void *, OSSerialize * serializer)
681 {
682 bool ok;
683
684 NVRAMREADLOCK();
685 ok = _stats->serialize(serializer);
686 NVRAMUNLOCK();
687
688 return ok;
689 }
690
691 // ************************** IODTNVRAMVariables ****************************
692
693 // private IOService based class for publishing distinct dictionary properties on
694 // for easy ioreg access since the serializeProperties call is overloaded and is used
695 // as variable access
696 class IODTNVRAMVariables : public IOService
697 {
698 OSDeclareDefaultStructors(IODTNVRAMVariables)
699 private:
700 IODTNVRAM *_provider;
701 uuid_t _guid;
702
703 public:
704 bool init(const uuid_t guid);
705 virtual bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
706
707 virtual bool serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
708 virtual OSPtr<OSObject> copyProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
709 virtual OSObject *getProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
710 virtual bool setProperty(const OSSymbol *aKey, OSObject *anObject) APPLE_KEXT_OVERRIDE;
711 virtual IOReturn setProperties(OSObject *properties) APPLE_KEXT_OVERRIDE;
712 virtual void removeProperty(const OSSymbol *aKey) APPLE_KEXT_OVERRIDE;
713 };
714
OSDefineMetaClassAndStructors(IODTNVRAMVariables,IOService)715 OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
716
717 bool
718 IODTNVRAMVariables::init(const uuid_t guid)
719 {
720 require(super::init(), fail);
721
722 uuid_copy(_guid, guid);
723
724 return true;
725
726 fail:
727 return false;
728 }
729
730 bool
start(IOService * provider)731 IODTNVRAMVariables::start(IOService * provider)
732 {
733 if (!super::start(provider)) {
734 goto error;
735 }
736
737 _provider = OSDynamicCast(IODTNVRAM, provider);
738 if (_provider == nullptr) {
739 goto error;
740 }
741
742 registerService();
743
744 return true;
745
746 error:
747 stop(provider);
748
749 return false;
750 }
751
752 bool
serializeProperties(OSSerialize * s) const753 IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
754 {
755 const OSSymbol *key;
756 OSSharedPtr<OSDictionary> dict;
757 OSSharedPtr<OSCollectionIterator> iter;
758 OSSharedPtr<OSDictionary> localVariables;
759 bool ok = false;
760 bool systemGuid = uuid_compare(_guid, gAppleSystemVariableGuid) == 0;
761
762 if (systemGuid) {
763 localVariables = _provider->_systemDict;
764 } else {
765 localVariables = _provider->_commonDict;
766 }
767
768 if (localVariables == nullptr) {
769 goto exit;
770 }
771
772 dict = OSDictionary::withCapacity(localVariables->getCount());
773 if (dict == nullptr) {
774 DEBUG_ERROR("No dictionary\n");
775 goto exit;
776 }
777
778 iter = OSCollectionIterator::withCollection(localVariables.get());
779 if (iter == nullptr) {
780 DEBUG_ERROR("failed to create iterator\n");
781 goto exit;
782 }
783
784 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
785 if (verifyPermission(kIONVRAMOperationRead, _guid, key)) {
786 dict->setObject(key, localVariables->getObject(key));
787 }
788 }
789
790 ok = dict->serialize(s);
791
792 exit:
793 DEBUG_INFO("ok=%d\n", ok);
794 return ok;
795 }
796
797 OSPtr<OSObject>
copyProperty(const OSSymbol * aKey) const798 IODTNVRAMVariables::copyProperty(const OSSymbol *aKey) const
799 {
800 if (_provider && !skipKey(aKey)) {
801 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
802
803 return _provider->copyPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
804 } else {
805 return nullptr;
806 }
807 }
808
809 OSObject *
getProperty(const OSSymbol * aKey) const810 IODTNVRAMVariables::getProperty(const OSSymbol *aKey) const
811 {
812 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
813
814 return theObject.get();
815 }
816
817 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)818 IODTNVRAMVariables::setProperty(const OSSymbol *aKey, OSObject *anObject)
819 {
820 if (_provider) {
821 return _provider->setPropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy(), anObject) == kIOReturnSuccess;
822 } else {
823 return false;
824 }
825 }
826
827 IOReturn
setProperties(OSObject * properties)828 IODTNVRAMVariables::setProperties(OSObject *properties)
829 {
830 IOReturn ret = kIOReturnSuccess;
831 OSObject *object;
832 const OSSymbol *key;
833 OSDictionary *dict;
834 OSSharedPtr<OSCollectionIterator> iter;
835
836 if (_provider) {
837 dict = OSDynamicCast(OSDictionary, properties);
838 if (dict == nullptr) {
839 DEBUG_ERROR("Not a dictionary\n");
840 return kIOReturnBadArgument;
841 }
842
843 iter = OSCollectionIterator::withCollection(dict);
844 if (iter == nullptr) {
845 DEBUG_ERROR("Couldn't create iterator\n");
846 return kIOReturnBadArgument;
847 }
848
849 while (ret == kIOReturnSuccess) {
850 key = OSDynamicCast(OSSymbol, iter->getNextObject());
851 if (key == nullptr) {
852 break;
853 }
854
855 object = dict->getObject(key);
856 if (object == nullptr) {
857 continue;
858 }
859
860 ret = _provider->setPropertyWithGUIDAndName(_guid, key->getCStringNoCopy(), object);
861 }
862 } else {
863 ret = kIOReturnNotReady;
864 }
865
866 DEBUG_INFO("ret=%#08x\n", ret);
867
868 return ret;
869 }
870
871 void
removeProperty(const OSSymbol * aKey)872 IODTNVRAMVariables::removeProperty(const OSSymbol *aKey)
873 {
874 if (_provider) {
875 _provider->removePropertyWithGUIDAndName(_guid, aKey->getCStringNoCopy());
876 }
877 }
878
879 // ************************** Format Handlers ***************************
880
~IODTNVRAMFormatHandler()881 IODTNVRAMFormatHandler::~IODTNVRAMFormatHandler()
882 {
883 }
884
885 #include "IONVRAMCHRPHandler.cpp"
886
887 #include "IONVRAMV3Handler.cpp"
888
889 // **************************** IODTNVRAM *********************************
890
891 bool
init(IORegistryEntry * old,const IORegistryPlane * plane)892 IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
893 {
894 OSSharedPtr<OSDictionary> dict;
895
896 DEBUG_INFO("...\n");
897
898 require(super::init(old, plane), fail);
899
900 #if XNU_TARGET_OS_OSX
901 #if CONFIG_CSR
902 gInternalBuild = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0);
903 DEBUG_INFO("gInternalBuild = %d\n", gInternalBuild);
904 #endif // CONFIG_CSR
905 #endif // XNU_TARGET_OS_OSX
906
907 _variableLock = IORWLockAlloc();
908 require(_variableLock != nullptr, fail);
909
910 _controllerLock = IOLockAlloc();
911 require(_controllerLock != nullptr, fail);
912
913 // Clear the IORegistryEntry property table
914 dict = OSDictionary::withCapacity(1);
915 require(dict != nullptr, fail);
916
917 setPropertyTable(dict.get());
918 dict.reset();
919
920 return true;
921
922 fail:
923 return false;
924 }
925
926 bool
start(IOService * provider)927 IODTNVRAM::start(IOService *provider)
928 {
929 OSSharedPtr<OSNumber> version;
930
931 DEBUG_INFO("...\n");
932
933 require(super::start(provider), fail);
934
935 // Check if our overridden init function was called
936 // If not, skip any additional initialization being done here.
937 // This is not an error we just need to successfully exit this function to allow
938 // AppleEFIRuntime to proceed and take over operation
939 require_action(_controllerLock != nullptr, no_common, DEBUG_INFO("x86 init\n"));
940
941 _diags = new IODTNVRAMDiags;
942 if (!_diags || !_diags->init()) {
943 DEBUG_ERROR("Unable to create/init the diags service\n");
944 OSSafeReleaseNULL(_diags);
945 goto fail;
946 }
947
948 if (!_diags->attach(this)) {
949 DEBUG_ERROR("Unable to attach the diags service!\n");
950 OSSafeReleaseNULL(_diags);
951 goto fail;
952 }
953
954 if (!_diags->start(this)) {
955 DEBUG_ERROR("Unable to start the diags service!\n");
956 _diags->detach(this);
957 OSSafeReleaseNULL(_diags);
958 goto fail;
959 }
960
961 // This will load the proxied variable data which will call back into
962 // IODTNVRAM for the variable sets which will also update the system/common services
963 initImageFormat();
964
965 version = OSNumber::withNumber(_format->getVersion(), 32);
966 _diags->setProperty(kCurrentNVRAMVersionKey, version.get());
967
968 if (_format->getSystemUsed()) {
969 _systemService = new IODTNVRAMVariables;
970
971 if (!_systemService || !_systemService->init(gAppleSystemVariableGuid)) {
972 DEBUG_ERROR("Unable to start the system service!\n");
973 OSSafeReleaseNULL(_systemService);
974 goto no_system;
975 }
976
977 _systemService->setName("options-system");
978
979 if (!_systemService->attach(this)) {
980 DEBUG_ERROR("Unable to attach the system service!\n");
981 OSSafeReleaseNULL(_systemService);
982 goto no_system;
983 }
984
985 if (!_systemService->start(this)) {
986 DEBUG_ERROR("Unable to start the system service!\n");
987 _systemService->detach(this);
988 OSSafeReleaseNULL(_systemService);
989 goto no_system;
990 }
991 }
992
993 no_system:
994 _commonService = new IODTNVRAMVariables;
995
996 if (!_commonService || !_commonService->init(gAppleNVRAMGuid)) {
997 DEBUG_ERROR("Unable to start the common service!\n");
998 OSSafeReleaseNULL(_commonService);
999 goto no_common;
1000 }
1001
1002 _commonService->setName("options-common");
1003
1004 if (!_commonService->attach(this)) {
1005 DEBUG_ERROR("Unable to attach the common service!\n");
1006 OSSafeReleaseNULL(_commonService);
1007 goto no_common;
1008 }
1009
1010 if (!_commonService->start(this)) {
1011 DEBUG_ERROR("Unable to start the common service!\n");
1012 _commonService->detach(this);
1013 OSSafeReleaseNULL(_commonService);
1014 goto no_common;
1015 }
1016
1017 no_common:
1018 return true;
1019
1020 fail:
1021 stop(provider);
1022 return false;
1023 }
1024
1025 void
initImageFormat(void)1026 IODTNVRAM::initImageFormat(void)
1027 {
1028 OSSharedPtr<IORegistryEntry> entry;
1029 OSSharedPtr<OSObject> prop;
1030 const char *proxyDataKey = "nvram-proxy-data";
1031 const char *bankSizeKey = "nvram-bank-size";
1032 OSData *data;
1033 uint32_t size = 0;
1034
1035 entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
1036
1037 require(entry != nullptr, skip);
1038
1039 prop = entry->copyProperty(bankSizeKey);
1040 require(prop != nullptr, skip);
1041
1042 data = OSDynamicCast(OSData, prop.get());
1043 require(data != nullptr, skip);
1044
1045 size = *((uint32_t*)data->getBytesNoCopy());
1046 DEBUG_ALWAYS("NVRAM size is %u bytes\n", size);
1047
1048 prop = entry->copyProperty(proxyDataKey);
1049 require(prop != nullptr, skip);
1050
1051 data = OSDynamicCast(OSData, prop.get());
1052 require(data != nullptr, skip);
1053
1054 if (IONVRAMV3Handler::isValidImage((const uint8_t *)data->getBytesNoCopy(), size)) {
1055 _format = IONVRAMV3Handler::init(this, (const uint8_t *)data->getBytesNoCopy(), size, _commonDict, _systemDict);
1056 require_action(_format, skip, panic("IONVRAMV3Handler creation failed\n"));
1057 } else {
1058 _format = IONVRAMCHRPHandler::init(this, (const uint8_t *)data->getBytesNoCopy(), size, _commonDict, _systemDict);
1059 require_action(_format, skip, panic("IONVRAMCHRPHandler creation failed\n"));
1060 }
1061
1062 #if defined(RELEASE)
1063 if (entry != nullptr) {
1064 entry->removeProperty(proxyDataKey);
1065 }
1066 #endif
1067
1068 skip:
1069 _lastDeviceSync = 0;
1070 _freshInterval = true;
1071 }
1072
1073 void
registerNVRAMController(IONVRAMController * controller)1074 IODTNVRAM::registerNVRAMController(IONVRAMController *controller)
1075 {
1076 DEBUG_INFO("setting controller\n");
1077
1078 NVRAMWRITELOCK();
1079 CONTROLLERLOCK();
1080
1081 _format->setController(controller);
1082
1083 CONTROLLERUNLOCK();
1084 NVRAMUNLOCK();
1085
1086 return;
1087 }
1088
1089 bool
safeToSync(void)1090 IODTNVRAM::safeToSync(void)
1091 {
1092 AbsoluteTime delta;
1093 UInt64 delta_ns;
1094 SInt32 delta_secs;
1095
1096 // delta interval went by
1097 clock_get_uptime(&delta);
1098
1099 // Figure it in seconds.
1100 absolutetime_to_nanoseconds(delta, &delta_ns);
1101 delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
1102
1103 if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
1104 _lastDeviceSync = delta_secs;
1105 _freshInterval = false;
1106 return true;
1107 }
1108
1109 return false;
1110 }
1111
1112 void
syncInternal(bool rateLimit)1113 IODTNVRAM::syncInternal(bool rateLimit)
1114 {
1115 DEBUG_INFO("rateLimit=%d\n", rateLimit);
1116
1117 if (!SAFE_TO_LOCK()) {
1118 DEBUG_INFO("cannot lock\n");
1119 return;
1120 }
1121
1122 // Rate limit requests to sync. Drivers that need this rate limiting will
1123 // shadow the data and only write to flash when they get a sync call
1124 if (rateLimit) {
1125 if (safeToSync() == false) {
1126 DEBUG_INFO("safeToSync()=false\n");
1127 return;
1128 }
1129 }
1130
1131 DEBUG_INFO("Calling sync()\n");
1132
1133 NVRAMREADLOCK();
1134 CONTROLLERLOCK();
1135
1136 _format->sync();
1137
1138 CONTROLLERUNLOCK();
1139 NVRAMUNLOCK();
1140
1141 if (_diags) {
1142 OSSharedPtr<OSNumber> generation = OSNumber::withNumber(_format->getGeneration(), 32);
1143 _diags->setProperty(kCurrentGenerationCountKey, generation.get());
1144 }
1145 }
1146
1147 void
sync(void)1148 IODTNVRAM::sync(void)
1149 {
1150 syncInternal(false);
1151 }
1152
1153 bool
serializeProperties(OSSerialize * s) const1154 IODTNVRAM::serializeProperties(OSSerialize *s) const
1155 {
1156 const OSSymbol *key;
1157 OSSharedPtr<OSDictionary> systemDict, commonDict, dict;
1158 OSSharedPtr<OSCollectionIterator> iter;
1159 bool ok = false;
1160 unsigned int totalCapacity = 0;
1161
1162 NVRAMREADLOCK();
1163 if (_commonDict) {
1164 commonDict = OSDictionary::withDictionary(_commonDict.get());
1165 }
1166
1167 if (_systemDict) {
1168 systemDict = OSDictionary::withDictionary(_systemDict.get());
1169 }
1170 NVRAMUNLOCK();
1171
1172 totalCapacity += (commonDict != nullptr) ? commonDict->getCapacity() : 0;
1173 totalCapacity += (systemDict != nullptr) ? systemDict->getCapacity() : 0;
1174
1175 dict = OSDictionary::withCapacity(totalCapacity);
1176
1177 if (dict == nullptr) {
1178 DEBUG_ERROR("No dictionary\n");
1179 goto exit;
1180 }
1181
1182 // Copy system entries first if present then copy unique common entries
1183 if (systemDict != nullptr) {
1184 iter = OSCollectionIterator::withCollection(systemDict.get());
1185 if (iter == nullptr) {
1186 DEBUG_ERROR("failed to create iterator\n");
1187 goto exit;
1188 }
1189
1190 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1191 if (verifyPermission(kIONVRAMOperationRead, gAppleSystemVariableGuid, key)) {
1192 dict->setObject(key, systemDict->getObject(key));
1193 }
1194 }
1195
1196 iter.reset();
1197 }
1198
1199 if (commonDict != nullptr) {
1200 iter = OSCollectionIterator::withCollection(commonDict.get());
1201 if (iter == nullptr) {
1202 DEBUG_ERROR("failed to create common iterator\n");
1203 goto exit;
1204 }
1205
1206 while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
1207 if (dict->getObject(key) != nullptr) {
1208 // Skip non uniques
1209 continue;
1210 }
1211 if (verifyPermission(kIONVRAMOperationRead, gAppleNVRAMGuid, key)) {
1212 dict->setObject(key, commonDict->getObject(key));
1213 }
1214 }
1215 }
1216
1217 ok = dict->serialize(s);
1218
1219 exit:
1220 DEBUG_INFO("ok=%d\n", ok);
1221
1222 return ok;
1223 }
1224
1225 NVRAMPartitionType
getDictionaryType(const OSDictionary * dict) const1226 IODTNVRAM::getDictionaryType(const OSDictionary *dict) const
1227 {
1228 if (dict == _commonDict) {
1229 return kIONVRAMPartitionCommon;
1230 } else if (dict == _systemDict) {
1231 return kIONVRAMPartitionSystem;
1232 } else {
1233 return kIONVRAMPartitionTypeUnknown;
1234 }
1235 }
1236
1237 IOReturn
chooseDictionary(IONVRAMOperation operation,const uuid_t varGuid,const char * variableName,OSDictionary ** dict) const1238 IODTNVRAM::chooseDictionary(IONVRAMOperation operation, const uuid_t varGuid, const char *variableName, OSDictionary **dict) const
1239 {
1240 if (_systemDict != nullptr) {
1241 bool systemGuid = uuid_compare(varGuid, gAppleSystemVariableGuid) == 0;
1242
1243 if (variableInAllowList(variableName)) {
1244 DEBUG_INFO("Using system dictionary due to allow list\n");
1245 if (!systemGuid) {
1246 DEBUG_ERROR("System GUID NOT used for %s\n", variableName);
1247 }
1248 *dict = _systemDict.get();
1249 } else if (systemGuid) {
1250 DEBUG_INFO("Using system dictionary via GUID\n");
1251 *dict = _systemDict.get();
1252 } else {
1253 DEBUG_INFO("Using common dictionary\n");
1254 *dict = _commonDict.get();
1255 }
1256 return kIOReturnSuccess;
1257 } else if (_commonDict != nullptr) {
1258 DEBUG_INFO("Defaulting to common dictionary\n");
1259 *dict = _commonDict.get();
1260 return kIOReturnSuccess;
1261 }
1262
1263 return kIOReturnNotFound;
1264 }
1265
1266 IOReturn
flushDict(const uuid_t guid,IONVRAMOperation op)1267 IODTNVRAM::flushDict(const uuid_t guid, IONVRAMOperation op)
1268 {
1269 IOReturn ret = kIOReturnSuccess;
1270
1271 if ((_systemDict != nullptr) && (uuid_compare(guid, gAppleSystemVariableGuid) == 0)) {
1272 ret = _format->flush(guid, op);
1273
1274 DEBUG_INFO("system dictionary flushed, ret=%08x\n", ret);
1275 } else if ((_commonDict != nullptr) && (uuid_compare(guid, gAppleNVRAMGuid) == 0)) {
1276 ret = _format->flush(guid, op);
1277
1278 DEBUG_INFO("common dictionary flushed, ret=%08x\n", ret);
1279 }
1280
1281 return ret;
1282 }
1283
1284 bool
handleSpecialVariables(const char * name,const uuid_t guid,const OSObject * obj,IOReturn * error)1285 IODTNVRAM::handleSpecialVariables(const char *name, const uuid_t guid, const OSObject *obj, IOReturn *error)
1286 {
1287 IOReturn ret = kIOReturnSuccess;
1288 bool special = false;
1289
1290 NVRAMLOCKASSERTEXCLUSIVE();
1291
1292 // ResetNVRam flushes both regions in one call
1293 // Obliterate can flush either separately
1294 if (strcmp(name, "ObliterateNVRam") == 0) {
1295 special = true;
1296 ret = flushDict(guid, kIONVRAMOperationObliterate);
1297 } else if (strcmp(name, "ResetNVRam") == 0) {
1298 special = true;
1299 ret = flushDict(gAppleSystemVariableGuid, kIONVRAMOperationReset);
1300
1301 if (ret != kIOReturnSuccess) {
1302 goto exit;
1303 }
1304
1305 ret = flushDict(gAppleNVRAMGuid, kIONVRAMOperationReset);
1306 }
1307
1308 exit:
1309 if (error) {
1310 *error = ret;
1311 }
1312
1313 return special;
1314 }
1315
1316 OSSharedPtr<OSObject>
copyPropertyWithGUIDAndName(const uuid_t guid,const char * name) const1317 IODTNVRAM::copyPropertyWithGUIDAndName(const uuid_t guid, const char *name) const
1318 {
1319 IOReturn result;
1320 OSDictionary *dict;
1321 OSSharedPtr<OSObject> theObject = nullptr;
1322
1323 result = chooseDictionary(kIONVRAMOperationRead, guid, name, &dict);
1324 if (result != kIOReturnSuccess) {
1325 DEBUG_INFO("No dictionary\n");
1326 goto exit;
1327 }
1328
1329 if (!verifyPermission(kIONVRAMOperationRead, guid, name)) {
1330 DEBUG_INFO("Not privileged\n");
1331 goto exit;
1332 }
1333
1334 NVRAMREADLOCK();
1335 theObject.reset(dict->getObject(name), OSRetain);
1336 NVRAMUNLOCK();
1337
1338 if (_diags) {
1339 _diags->logVariable(getDictionaryType(dict), kIONVRAMOperationRead, name, NULL);
1340 }
1341
1342 if (theObject != nullptr) {
1343 DEBUG_INFO("found data\n");
1344 }
1345
1346 exit:
1347 return theObject;
1348 }
1349
1350 OSSharedPtr<OSObject>
copyProperty(const OSSymbol * aKey) const1351 IODTNVRAM::copyProperty(const OSSymbol *aKey) const
1352 {
1353 const char *variableName;
1354 uuid_t varGuid;
1355
1356 if (skipKey(aKey)) {
1357 return nullptr;
1358 }
1359 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1360
1361 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1362
1363 return copyPropertyWithGUIDAndName(varGuid, variableName);
1364 }
1365
1366 OSSharedPtr<OSObject>
copyProperty(const char * aKey) const1367 IODTNVRAM::copyProperty(const char *aKey) const
1368 {
1369 OSSharedPtr<const OSSymbol> keySymbol;
1370 OSSharedPtr<OSObject> theObject;
1371
1372 keySymbol = OSSymbol::withCString(aKey);
1373 if (keySymbol != nullptr) {
1374 theObject = copyProperty(keySymbol.get());
1375 }
1376
1377 return theObject;
1378 }
1379
1380 OSObject *
getProperty(const OSSymbol * aKey) const1381 IODTNVRAM::getProperty(const OSSymbol *aKey) const
1382 {
1383 // The shared pointer gets released at the end of the function,
1384 // and returns a view into theObject.
1385 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1386
1387 return theObject.get();
1388 }
1389
1390 OSObject *
getProperty(const char * aKey) const1391 IODTNVRAM::getProperty(const char *aKey) const
1392 {
1393 // The shared pointer gets released at the end of the function,
1394 // and returns a view into theObject.
1395 OSSharedPtr<OSObject> theObject = copyProperty(aKey);
1396
1397 return theObject.get();
1398 }
1399
1400 IOReturn
setPropertyWithGUIDAndName(const uuid_t guid,const char * name,OSObject * anObject)1401 IODTNVRAM::setPropertyWithGUIDAndName(const uuid_t guid, const char *name, OSObject *anObject)
1402 {
1403 IOReturn ret = kIOReturnSuccess;
1404 bool remove = false;
1405 OSString *tmpString = nullptr;
1406 OSSharedPtr<OSObject> propObject;
1407 OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
1408 bool deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
1409 bool ok;
1410 size_t propDataSize = 0;
1411
1412 deletePropertyKey = strncmp(name, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
1413 syncNowPropertyKey = strncmp(name, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
1414 forceSyncNowPropertyKey = strncmp(name, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
1415
1416 if (deletePropertyKey) {
1417 tmpString = OSDynamicCast(OSString, anObject);
1418 if (tmpString != nullptr) {
1419 const char *variableName;
1420 uuid_t valueVarGuid;
1421 bool guidProvided;
1422 IOReturn removeRet;
1423
1424 guidProvided = parseVariableName(tmpString->getCStringNoCopy(), &valueVarGuid, &variableName);
1425
1426 // nvram tool will provide a "nvram -d var" or "nvram -d guid:var" as
1427 // kIONVRAMDeletePropertyKey=var or kIONVRAMDeletePropertyKey=guid:var
1428 // that will come into this function as (gAppleNVRAMGuid, varname, nullptr)
1429 // if we provide the "-z" flag to the nvram tool this function will come in as
1430 // (gAppleSystemVariableGuid, varname, nullptr). We are reparsing the value string,
1431 // if there is a GUID provided with the value then use that GUID otherwise use the
1432 // guid that was provided via the node selection or default.
1433 if (guidProvided == false) {
1434 DEBUG_INFO("Removing with API provided GUID\n");
1435 removeRet = removePropertyWithGUIDAndName(guid, variableName);
1436 } else {
1437 DEBUG_INFO("Removing with value provided GUID\n");
1438 removeRet = removePropertyWithGUIDAndName(valueVarGuid, variableName);
1439 }
1440
1441 DEBUG_INFO("kIONVRAMDeletePropertyKey found, removeRet=%#08x\n", removeRet);
1442 } else {
1443 DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1444 ret = kIOReturnError;
1445 }
1446 goto exit;
1447 } else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
1448 tmpString = OSDynamicCast(OSString, anObject);
1449 DEBUG_INFO("NVRAM sync key %s found\n", name);
1450 if (tmpString != nullptr) {
1451 // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1452 syncInternal(syncNowPropertyKey);
1453 } else {
1454 DEBUG_INFO("%s value needs to be an OSString\n", name);
1455 ret = kIOReturnError;
1456 }
1457 goto exit;
1458 }
1459
1460 if (!verifyPermission(kIONVRAMOperationWrite, guid, name)) {
1461 DEBUG_INFO("Not privileged\n");
1462 ret = kIOReturnNotPrivileged;
1463 goto exit;
1464 }
1465
1466 // Make sure the object is of the correct type.
1467 switch (getVariableType(name)) {
1468 case kOFVariableTypeBoolean:
1469 propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
1470 break;
1471
1472 case kOFVariableTypeNumber:
1473 propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
1474 break;
1475
1476 case kOFVariableTypeString:
1477 propObject = OSDynamicPtrCast<OSString>(sharedObject);
1478 if (propObject != nullptr) {
1479 propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
1480
1481 if ((strncmp(name, kIONVRAMBootArgsKey, sizeof(kIONVRAMBootArgsKey)) == 0) && (propDataSize >= BOOT_LINE_LENGTH)) {
1482 DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
1483 ret = kIOReturnNoSpace;
1484 goto exit;
1485 }
1486 }
1487 break;
1488
1489 case kOFVariableTypeData:
1490 propObject = OSDynamicPtrCast<OSData>(sharedObject);
1491 if (propObject == nullptr) {
1492 tmpString = OSDynamicCast(OSString, sharedObject.get());
1493 if (tmpString != nullptr) {
1494 propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
1495 tmpString->getLength());
1496 }
1497 }
1498
1499 if (propObject != nullptr) {
1500 propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
1501 }
1502
1503 #if defined(XNU_TARGET_OS_OSX)
1504 if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
1505 remove = true;
1506 }
1507 #endif /* defined(XNU_TARGET_OS_OSX) */
1508 break;
1509 default:
1510 break;
1511 }
1512
1513 if (propObject == nullptr) {
1514 DEBUG_INFO("No property object\n");
1515 ret = kIOReturnBadArgument;
1516 goto exit;
1517 }
1518
1519 if (!verifyWriteSizeLimit(guid, name, propDataSize)) {
1520 DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, name);
1521 ret = kIOReturnNoSpace;
1522 goto exit;
1523 }
1524
1525 NVRAMWRITELOCK();
1526 ok = handleSpecialVariables(name, guid, propObject.get(), &ret);
1527 NVRAMUNLOCK();
1528
1529 if (ok) {
1530 goto exit;
1531 }
1532
1533 if (remove == false) {
1534 DEBUG_INFO("Adding object\n");
1535 NVRAMWRITELOCK();
1536 ret = _format->setVariable(guid, name, propObject.get());
1537 NVRAMUNLOCK();
1538 } else {
1539 DEBUG_INFO("Removing object\n");
1540 ret = removePropertyWithGUIDAndName(guid, name);
1541 }
1542
1543 if (tmpString) {
1544 propObject.reset();
1545 }
1546
1547 exit:
1548 DEBUG_INFO("ret=%#08x\n", ret);
1549
1550 return ret;
1551 }
1552
1553 IOReturn
setPropertyInternal(const OSSymbol * aKey,OSObject * anObject)1554 IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
1555 {
1556 const char *variableName;
1557 uuid_t varGuid;
1558
1559 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1560
1561 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1562
1563 return setPropertyWithGUIDAndName(varGuid, variableName, anObject);
1564 }
1565
1566 bool
setProperty(const OSSymbol * aKey,OSObject * anObject)1567 IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
1568 {
1569 return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
1570 }
1571
1572 void
removeProperty(const OSSymbol * aKey)1573 IODTNVRAM::removeProperty(const OSSymbol *aKey)
1574 {
1575 IOReturn ret;
1576
1577 ret = removePropertyInternal(aKey);
1578
1579 if (ret != kIOReturnSuccess) {
1580 DEBUG_INFO("removePropertyInternal failed, ret=%#08x\n", ret);
1581 }
1582 }
1583
1584 IOReturn
removePropertyWithGUIDAndName(const uuid_t guid,const char * name)1585 IODTNVRAM::removePropertyWithGUIDAndName(const uuid_t guid, const char *name)
1586 {
1587 IOReturn ret = kIOReturnSuccess;
1588 uuid_string_t uuidString;
1589
1590 uuid_unparse(guid, uuidString);
1591
1592 DEBUG_INFO("%s:%s\n", uuidString, name);
1593
1594 if (!verifyPermission(kIONVRAMOperationDelete, guid, name)) {
1595 DEBUG_INFO("Not priveleged\n");
1596 ret = kIOReturnNotPrivileged;
1597 goto exit;
1598 }
1599
1600 NVRAMWRITELOCK();
1601
1602 if (_format->setVariable(guid, name, nullptr) != kIOReturnSuccess) {
1603 DEBUG_INFO("%s not found\n", name);
1604 ret = kIOReturnNotFound;
1605 }
1606
1607 NVRAMUNLOCK();
1608
1609 exit:
1610 return ret;
1611 }
1612
1613 IOReturn
removePropertyInternal(const OSSymbol * aKey)1614 IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
1615 {
1616 IOReturn ret;
1617 const char *variableName;
1618 uuid_t varGuid;
1619
1620 DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
1621
1622 parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
1623
1624 ret = removePropertyWithGUIDAndName(varGuid, variableName);
1625
1626 return ret;
1627 }
1628
1629 IOReturn
setProperties(OSObject * properties)1630 IODTNVRAM::setProperties(OSObject *properties)
1631 {
1632 IOReturn ret = kIOReturnSuccess;
1633 OSObject *object;
1634 const OSSymbol *key;
1635 OSDictionary *dict;
1636 OSSharedPtr<OSCollectionIterator> iter;
1637
1638 dict = OSDynamicCast(OSDictionary, properties);
1639 if (dict == nullptr) {
1640 DEBUG_ERROR("Not a dictionary\n");
1641 return kIOReturnBadArgument;
1642 }
1643
1644 iter = OSCollectionIterator::withCollection(dict);
1645 if (iter == nullptr) {
1646 DEBUG_ERROR("Couldn't create iterator\n");
1647 return kIOReturnBadArgument;
1648 }
1649
1650 while (ret == kIOReturnSuccess) {
1651 key = OSDynamicCast(OSSymbol, iter->getNextObject());
1652 if (key == nullptr) {
1653 break;
1654 }
1655
1656 object = dict->getObject(key);
1657 if (object == nullptr) {
1658 continue;
1659 }
1660
1661 ret = setPropertyInternal(key, object);
1662 }
1663
1664 DEBUG_INFO("ret=%#08x\n", ret);
1665
1666 return ret;
1667 }
1668
1669 // ********************** Deprecated ********************
1670
1671 IOReturn
readXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)1672 IODTNVRAM::readXPRAM(IOByteCount offset, uint8_t *buffer,
1673 IOByteCount length)
1674 {
1675 return kIOReturnUnsupported;
1676 }
1677
1678 IOReturn
writeXPRAM(IOByteCount offset,uint8_t * buffer,IOByteCount length)1679 IODTNVRAM::writeXPRAM(IOByteCount offset, uint8_t *buffer,
1680 IOByteCount length)
1681 {
1682 return kIOReturnUnsupported;
1683 }
1684
1685 IOReturn
readNVRAMProperty(IORegistryEntry * entry,const OSSymbol ** name,OSData ** value)1686 IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
1687 const OSSymbol **name,
1688 OSData **value)
1689 {
1690 return kIOReturnUnsupported;
1691 }
1692
1693 IOReturn
writeNVRAMProperty(IORegistryEntry * entry,const OSSymbol * name,OSData * value)1694 IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
1695 const OSSymbol *name,
1696 OSData *value)
1697 {
1698 return kIOReturnUnsupported;
1699 }
1700
1701 OSDictionary *
getNVRAMPartitions(void)1702 IODTNVRAM::getNVRAMPartitions(void)
1703 {
1704 return NULL;
1705 }
1706
1707 IOReturn
readNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)1708 IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
1709 IOByteCount offset, uint8_t *buffer,
1710 IOByteCount length)
1711 {
1712 return kIOReturnUnsupported;
1713 }
1714
1715 IOReturn
writeNVRAMPartition(const OSSymbol * partitionID,IOByteCount offset,uint8_t * buffer,IOByteCount length)1716 IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
1717 IOByteCount offset, uint8_t *buffer,
1718 IOByteCount length)
1719 {
1720 return kIOReturnUnsupported;
1721 }
1722
1723 IOByteCount
savePanicInfo(uint8_t * buffer,IOByteCount length)1724 IODTNVRAM::savePanicInfo(uint8_t *buffer, IOByteCount length)
1725 {
1726 return 0;
1727 }
1728