2 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
5 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
27 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #define IOKIT_ENABLE_SHARED_PTR
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 <pexpert/boot.h>
42 #include <pexpert/pexpert.h>
44 #define super IOService
46 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
49 #define NVRAM_CHRP_SIG_APPLE 0x5A
50 #define NVRAM_CHRP_APPLE_HEADER_NAME "nvram"
52 // From Apple CHRP Spec
53 #define NVRAM_CHRP_SIG_SYSTEM 0x70
54 #define NVRAM_CHRP_SIG_CONFIG 0x71
55 #define NVRAM_CHRP_SIG_FREESPACE 0x7F
57 #define NVRAM_CHRP_PARTITION_NAME_COMMON "common"
58 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM "system"
59 #define NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY "secure"
60 #define NVRAM_CHRP_PARTITION_NAME_FREESPACE "\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77"
62 #define NVRAM_CHRP_LENGTH_BLOCK_SIZE 0x10 // CHRP length field is in 16 byte blocks
64 typedef struct chrp_nvram_header
{ //16 bytes
66 uint8_t cksum
; // checksum on sig, len, and name
67 uint16_t len
; // total length of the partition in 16 byte blocks starting with the signature
68 // and ending with the last byte of data area, ie len includes its own header size
71 } chrp_nvram_header_t
;
73 typedef struct apple_nvram_header
{ // 16 + 16 bytes
74 struct chrp_nvram_header chrp
;
78 } apple_nvram_header_t
;
81 #define kIONVRAMPrivilege kIOClientPrivilegeAdministrator
83 OSDefineMetaClassAndStructors(IODTNVRAM
, IOService
);
85 #if defined(DEBUG) || defined(DEVELOPMENT)
86 #define DEBUG_INFO(fmt, args...) \
89 IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \
92 #define DEBUG_ALWAYS(fmt, args...) \
94 IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \
97 #define DEBUG_INFO(fmt, args...)
98 #define DEBUG_ALWAYS(fmt, args...)
101 #define DEBUG_ERROR DEBUG_ALWAYS
103 #define CONTROLLERLOCK() \
105 if (preemption_enabled() && !panic_active()) \
106 IOLockLock(_controllerLock); \
109 #define CONTROLLERUNLOCK() \
111 if (preemption_enabled() && !panic_active()) \
112 IOLockUnlock(_controllerLock); \
115 #define NVRAMLOCK() \
117 if (preemption_enabled() && !panic_active()) \
118 IOLockLock(_variableLock); \
121 #define NVRAMUNLOCK() \
123 if (preemption_enabled() && !panic_active()) \
124 IOLockUnlock(_variableLock); \
127 #define NVRAMLOCKASSERT() \
129 if (preemption_enabled() && !panic_active()) \
130 IOLockAssert(_variableLock, kIOLockAssertOwned); \
137 OSSharedPtr
<OSDictionary
> &dict
;
141 // Guid for Apple System Boot variables
142 // 40A0DDD2-77F8-4392-B4A3-1E7304206516
143 UUID_DEFINE(gAppleSystemVariableGuid
, 0x40, 0xA0, 0xDD, 0xD2, 0x77, 0xF8, 0x43, 0x92, 0xB4, 0xA3, 0x1E, 0x73, 0x04, 0x20, 0x65, 0x16);
145 // Apple NVRAM Variable namespace (APPLE_VENDOR_OS_VARIABLE_GUID)
146 // 7C436110-AB2A-4BBB-A880-FE41995C9F82
147 UUID_DEFINE(gAppleNVRAMGuid
, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82);
149 static bool gNVRAMLogging
= false;
151 // allowlist variables from macboot that need to be set/get from system region if present
152 static const char * const gNVRAMSystemList
[] = {
162 "auto-boot-halt-stage",
184 "com.apple.System.boot-nonce",
185 "com.apple.System.rtc-offset",
186 "com.apple.System.tz0-size",
197 "display-color-space",
200 "dpcd-max-brightness",
208 "enable-upgrade-fallback",
212 "failboot-breadcrumbs",
215 "force-upgrade-fail",
220 "iboot-failure-reason",
221 "iboot-failure-reason-str",
222 "iboot-failure-volume",
223 "iboot1-precommitted",
232 "mipi-bridge-cmd-verify",
233 "mipi-bridge-poll-cmd-fifo",
235 "one-time-boot-command",
240 "panicmedic-threshold",
241 "panicmedic-timestamps",
245 "policy-nonce-digests",
246 "preserve-debuggability",
247 "prevent-restores", // Keep for factory <rdar://problem/70476321>
249 "ramrod-kickstart-aces",
253 "reconfig-breakpoints",
254 "recovery-boot-mode",
255 "recovery-breadcrumbs",
256 "restored-host-timeout",
261 "StartupMuteAccessibility",
262 "storage-prev-assert",
263 "storage-prev-assert-stored",
266 "SystemAudioVolumeExtension",
267 "SystemAudioVolumeSaved",
269 "upgrade-fallback-boot-command",
279 IONVRAMVariableType type
;
283 VariableTypeEntry gVariableTypes
[] = {
284 {"auto-boot?", kOFVariableTypeBoolean
},
285 {"boot-args", kOFVariableTypeString
},
286 {"boot-command", kOFVariableTypeString
},
287 {"boot-device", kOFVariableTypeString
},
288 {"boot-file", kOFVariableTypeString
},
289 {"boot-screen", kOFVariableTypeString
},
290 {"boot-script", kOFVariableTypeString
},
291 {"console-screen", kOFVariableTypeString
},
292 {"default-client-ip", kOFVariableTypeString
},
293 {"default-gateway-ip", kOFVariableTypeString
},
294 {"default-mac-address?", kOFVariableTypeBoolean
},
295 {"default-router-ip", kOFVariableTypeString
},
296 {"default-server-ip", kOFVariableTypeString
},
297 {"default-subnet-mask", kOFVariableTypeString
},
298 {"diag-device", kOFVariableTypeString
},
299 {"diag-file", kOFVariableTypeString
},
300 {"diag-switch?", kOFVariableTypeBoolean
},
301 {"fcode-debug?", kOFVariableTypeBoolean
},
302 {"input-device", kOFVariableTypeString
},
303 {"input-device-1", kOFVariableTypeString
},
304 {"little-endian?", kOFVariableTypeBoolean
},
305 {"load-base", kOFVariableTypeNumber
},
306 {"mouse-device", kOFVariableTypeString
},
307 {"nvramrc", kOFVariableTypeString
},
308 {"oem-banner", kOFVariableTypeString
},
309 {"oem-banner?", kOFVariableTypeBoolean
},
310 {"oem-logo", kOFVariableTypeString
},
311 {"oem-logo?", kOFVariableTypeBoolean
},
312 {"output-device", kOFVariableTypeString
},
313 {"output-device-1", kOFVariableTypeString
},
314 {"pci-probe-list", kOFVariableTypeNumber
},
315 {"pci-probe-mask", kOFVariableTypeNumber
},
316 {"real-base", kOFVariableTypeNumber
},
317 {"real-mode?", kOFVariableTypeBoolean
},
318 {"real-size", kOFVariableTypeNumber
},
319 {"screen-#columns", kOFVariableTypeNumber
},
320 {"screen-#rows", kOFVariableTypeNumber
},
321 {"security-mode", kOFVariableTypeString
},
322 {"selftest-#megs", kOFVariableTypeNumber
},
323 {"use-generic?", kOFVariableTypeBoolean
},
324 {"use-nvramrc?", kOFVariableTypeBoolean
},
325 {"virt-base", kOFVariableTypeNumber
},
326 {"virt-size", kOFVariableTypeNumber
},
328 #if !defined(__x86_64__)
329 {"acc-cm-override-charger-count", kOFVariableTypeNumber
},
330 {"acc-cm-override-count", kOFVariableTypeNumber
},
331 {"acc-mb-ld-lifetime", kOFVariableTypeNumber
},
332 {"com.apple.System.boot-nonce", kOFVariableTypeString
},
333 {"darkboot", kOFVariableTypeBoolean
},
334 {"enter-tdm-mode", kOFVariableTypeBoolean
},
335 #endif /* !defined(__x86_64__) */
336 {nullptr, kOFVariableTypeData
} // Default type to return
339 union VariablePermission
{
341 uint64_t UserWrite
:1;
342 uint64_t RootRequired
:1;
343 uint64_t KernelOnly
:1;
344 uint64_t ResetNVRAMOnlyDelete
:1;
345 uint64_t NeverAllowedToDelete
:1;
346 uint64_t FullAccess
:1;
347 uint64_t Reserved
:58;
354 VariablePermission p
;
355 } VariablePermissionEntry
;
358 VariablePermissionEntry gVariablePermissions
[] = {
359 {"aapl,pci", .p
.Bits
.RootRequired
= 1},
360 {"battery-health", .p
.Bits
.RootRequired
= 1,
361 .p
.Bits
.NeverAllowedToDelete
= 1},
362 {"boot-image", .p
.Bits
.UserWrite
= 1},
363 {"com.apple.System.fp-state", .p
.Bits
.KernelOnly
= 1},
364 {"policy-nonce-digests", .p
.Bits
.ResetNVRAMOnlyDelete
= 1},
365 {"security-password", .p
.Bits
.RootRequired
= 1},
367 #if !defined(__x86_64__)
368 {"acc-cm-override-charger-count", .p
.Bits
.KernelOnly
= 1},
369 {"acc-cm-override-count", .p
.Bits
.KernelOnly
= 1},
370 {"acc-mb-ld-lifetime", .p
.Bits
.KernelOnly
= 1},
371 {"backlight-level", .p
.Bits
.UserWrite
= 1},
372 {"com.apple.System.boot-nonce", .p
.Bits
.KernelOnly
= 1},
373 {"com.apple.System.sep.art", .p
.Bits
.KernelOnly
= 1},
374 {"darkboot", .p
.Bits
.UserWrite
= 1},
375 {"nonce-seeds", .p
.Bits
.KernelOnly
= 1},
376 #endif /* !defined(__x86_64__) */
378 {nullptr, {.Bits
.FullAccess
= 1}} // Default access
381 static IONVRAMVariableType
382 getVariableType(const char *propName
)
384 const VariableTypeEntry
*entry
;
386 entry
= gVariableTypes
;
387 while (entry
->name
!= nullptr) {
388 if (strcmp(entry
->name
, propName
) == 0) {
397 static IONVRAMVariableType
398 getVariableType(const OSSymbol
*propSymbol
)
400 return getVariableType(propSymbol
->getCStringNoCopy());
403 static VariablePermission
404 getVariablePermission(const char *propName
)
406 const VariablePermissionEntry
*entry
;
408 entry
= gVariablePermissions
;
409 while (entry
->name
!= nullptr) {
410 if (strcmp(entry
->name
, propName
) == 0) {
420 variableInAllowList(const char *varName
)
424 while (gNVRAMSystemList
[i
] != nullptr) {
425 if (strcmp(varName
, gNVRAMSystemList
[i
]) == 0) {
435 verifyWriteSizeLimit(const uuid_t
*varGuid
, const char *variableName
, size_t propDataSize
)
437 if (variableInAllowList(variableName
)) {
438 if (strnstr(variableName
, "breadcrumbs", strlen(variableName
)) != NULL
) {
439 return propDataSize
<= 1024;
441 return propDataSize
<= 768;
449 verifyPermission(IONVRAMOperation op
, const uuid_t
*varGuid
, const char *varName
)
451 VariablePermission perm
;
452 bool kernel
, admin
, writeEntitled
, readEntitled
, allowList
, systemGuid
, systemEntitled
;
454 perm
= getVariablePermission(varName
);
456 kernel
= current_task() == kernel_task
;
458 if (perm
.Bits
.KernelOnly
) {
459 DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName
, kernel
);
463 allowList
= variableInAllowList(varName
);
464 systemGuid
= uuid_compare(*varGuid
, gAppleSystemVariableGuid
) == 0;
465 admin
= IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege
) == kIOReturnSuccess
;
466 writeEntitled
= IOTaskHasEntitlement(current_task(), kIONVRAMWriteAccessKey
);
467 readEntitled
= IOTaskHasEntitlement(current_task(), kIONVRAMReadAccessKey
);
468 systemEntitled
= IOTaskHasEntitlement(current_task(), kIONVRAMSystemAllowKey
) || kernel
;
471 case kIONVRAMOperationRead
:
472 if (kernel
|| admin
|| readEntitled
|| perm
.Bits
.FullAccess
) {
477 case kIONVRAMOperationWrite
:
478 if (kernel
|| perm
.Bits
.UserWrite
|| admin
|| writeEntitled
) {
481 if (!systemEntitled
) {
482 DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName
);
484 } else if (!systemEntitled
) {
485 DEBUG_ERROR("Not entitled for system region writes for %s\n", varName
);
493 case kIONVRAMOperationDelete
:
494 case kIONVRAMOperationObliterate
:
495 case kIONVRAMOperationReset
:
496 if (perm
.Bits
.NeverAllowedToDelete
) {
497 DEBUG_INFO("Never allowed to delete %s\n", varName
);
499 } else if ((op
== kIONVRAMOperationObliterate
) && perm
.Bits
.ResetNVRAMOnlyDelete
) {
500 DEBUG_INFO("Not allowed to obliterate %s\n", varName
);
504 if (kernel
|| perm
.Bits
.UserWrite
|| admin
|| writeEntitled
) {
507 if (!systemEntitled
) {
508 DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName
);
510 } else if (!systemEntitled
) {
511 DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName
);
520 DEBUG_INFO("Permission for %s denied, kernel=%d, admin=%d, writeEntitled=%d, readEntitled=%d, systemGuid=%d, systemEntitled=%d\n",
521 varName
, kernel
, admin
, writeEntitled
, readEntitled
, systemGuid
, systemEntitled
);
526 verifyPermission(IONVRAMOperation op
, const uuid_t
*varGuid
, const OSSymbol
*varName
)
528 return verifyPermission(op
, varGuid
, varName
->getCStringNoCopy());
532 * Parse a variable name of the form "GUID:name".
533 * If the name cannot be parsed, substitute the Apple global variable GUID.
534 * Returns TRUE if a GUID was found in the name, FALSE otherwise.
535 * The guidResult and nameResult arguments may be nullptr if you just want
536 * to check the format of the string.
539 parseVariableName(const char *key
, uuid_t
*guidResult
, const char **nameResult
)
541 uuid_string_t temp
= {0};
542 size_t keyLen
= strlen(key
);
544 const char *name
= key
;
547 if (keyLen
> sizeof(temp
)) {
548 // check for at least UUID + ":" + more
549 memcpy(temp
, key
, sizeof(temp
) - 1);
551 if ((uuid_parse(temp
, guid
) == 0) &&
552 (key
[sizeof(temp
) - 1] == ':')) {
553 name
= key
+ sizeof(temp
);
559 result
? uuid_copy(*guidResult
, guid
) : uuid_copy(*guidResult
, gAppleNVRAMGuid
);
568 // private IOService based class for publishing distinct dictionary properties on
569 // for easy ioreg access since the serializeProperties call is overloaded and is used
570 // as variable access
571 class IODTNVRAMVariables
: public IOService
573 OSDeclareDefaultStructors(IODTNVRAMVariables
)
575 IODTNVRAM
*_provider
;
576 OSDictionary
*_properties
;
580 bool init(const uuid_t
*guid
);
581 virtual bool start(IOService
* provider
) APPLE_KEXT_OVERRIDE
;
582 virtual IOReturn
setProperties(OSObject
* properties
) APPLE_KEXT_OVERRIDE
;
583 virtual bool serializeProperties(OSSerialize
*s
) const APPLE_KEXT_OVERRIDE
;
586 OSDefineMetaClassAndStructors(IODTNVRAMVariables
, IOService
)
589 IODTNVRAMVariables::init(const uuid_t
*guid
)
591 require(super::init(), error
);
592 require(guid
, error
);
594 uuid_copy(_guid
, *guid
);
603 IODTNVRAMVariables::start(IOService
* provider
)
605 require(IOService::start(provider
), error
);
607 require(_provider
= OSDynamicCast(IODTNVRAM
, provider
), error
);
620 IODTNVRAMVariables::setProperties(OSObject
* properties
)
622 if (OSDynamicCast(OSDictionary
, properties
)) {
623 OSSafeReleaseNULL(_properties
);
624 _properties
= OSDynamicCast(OSDictionary
, properties
);
625 properties
->retain();
628 return IOService::setProperties(properties
);
632 IODTNVRAMVariables::serializeProperties(OSSerialize
*s
) const
635 OSSharedPtr
<OSDictionary
> dict
;
636 OSSharedPtr
<OSCollectionIterator
> iter
;
637 OSSharedPtr
<OSDictionary
> localProperties(_properties
, OSRetain
);
640 require(localProperties
!= nullptr, exit
);
642 dict
= OSDictionary::withCapacity(localProperties
->getCount());
643 require_action(dict
, exit
, DEBUG_ERROR("No dictionary\n"));
645 iter
= OSCollectionIterator::withCollection(localProperties
.get());
646 require_action(iter
, exit
, DEBUG_ERROR("failed to create iterator\n"));
648 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
649 if (verifyPermission(kIONVRAMOperationRead
, &_guid
, key
)) {
650 dict
->setObject(key
, localProperties
->getObject(key
));
654 result
= dict
->serialize(s
);
657 DEBUG_INFO("result=%d\n", result
);
662 IODTNVRAM::init(IORegistryEntry
*old
, const IORegistryPlane
*plane
)
664 OSSharedPtr
<OSDictionary
> dict
;
666 if (!super::init(old
, plane
)) {
670 _variableLock
= IOLockAlloc();
671 if (!_variableLock
) {
675 _controllerLock
= IOLockAlloc();
676 if (!_controllerLock
) {
680 PE_parse_boot_argn("nvram-log", &gNVRAMLogging
, sizeof(gNVRAMLogging
));
682 dict
= OSDictionary::withCapacity(1);
683 if (dict
== nullptr) {
686 setPropertyTable(dict
.get());
689 _nvramSize
= getNVRAMSize();
690 if (_nvramSize
== 0) {
691 DEBUG_ERROR("NVRAM : Error - default size not specified in DT\n");
694 // partition offsets are UInt16 (bytes / 0x10) + 1
695 if (_nvramSize
> 0xFFFF * 0x10) {
696 DEBUG_ERROR("NVRAM : truncating _nvramSize from %ld\n", (long) _nvramSize
);
697 _nvramSize
= 0xFFFF * 0x10;
699 _nvramImage
= IONew(UInt8
, _nvramSize
);
700 if (_nvramImage
== nullptr) {
704 _nvramPartitionOffsets
= OSDictionary::withCapacity(1);
705 if (_nvramPartitionOffsets
== nullptr) {
709 _nvramPartitionLengths
= OSDictionary::withCapacity(1);
710 if (_nvramPartitionLengths
== nullptr) {
714 _registryPropertiesKey
= OSSymbol::withCStringNoCopy("aapl,pci");
715 if (_registryPropertiesKey
== nullptr) {
719 // <rdar://problem/9529235> race condition possible between
720 // IODTNVRAM and IONVRAMController (restore loses boot-args)
723 // Require at least the common partition to be present and error free
724 if (_commonDict
== nullptr) {
732 IODTNVRAM::initProxyData(void)
734 OSSharedPtr
<IORegistryEntry
> entry
;
735 const char *key
= "nvram-proxy-data";
739 entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
);
740 if (entry
!= nullptr) {
741 OSSharedPtr
<OSObject
> prop
= entry
->copyProperty(key
);
742 if (prop
!= nullptr) {
743 data
= OSDynamicCast(OSData
, prop
.get());
744 if (data
!= nullptr) {
745 bytes
= data
->getBytesNoCopy();
746 if ((bytes
!= nullptr) && (data
->getLength() <= _nvramSize
)) {
747 bcopy(bytes
, _nvramImage
, data
->getLength());
753 entry
->removeProperty(key
);
758 IODTNVRAM::getNVRAMSize(void)
760 OSSharedPtr
<IORegistryEntry
> entry
;
761 const char *key
= "nvram-total-size";
765 entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
);
766 if (entry
!= nullptr) {
767 OSSharedPtr
<OSObject
> prop
= entry
->copyProperty(key
);
768 if (prop
!= nullptr) {
769 data
= OSDynamicCast(OSData
, prop
.get());
770 if (data
!= nullptr) {
771 size
= *((UInt32
*)data
->getBytesNoCopy());
772 DEBUG_ALWAYS("NVRAM size is %u bytes\n", (unsigned int) size
);
781 IODTNVRAM::registerNVRAMController(IONVRAMController
*nvram
)
785 if (_nvramController
!= nullptr) {
786 DEBUG_ERROR("Duplicate controller set\n");
790 DEBUG_INFO("setting controller\n");
792 _nvramController
= nvram
;
794 // <rdar://problem/9529235> race condition possible between
795 // IODTNVRAM and IONVRAMController (restore loses boot-args)
797 DEBUG_INFO("Proxied NVRAM data\n");
798 _nvramController
->read(0, _nvramImage
, _nvramSize
);
802 if (_systemPartitionSize
) {
803 _systemService
= new IODTNVRAMVariables
;
805 if (!_systemService
|| !_systemService
->init(&gAppleSystemVariableGuid
)) {
806 DEBUG_ERROR("Unable to start the system service!\n");
810 _systemService
->setName("options-system");
812 if (!_systemService
->attach(this)) {
813 DEBUG_ERROR("Unable to attach the system service!\n");
814 OSSafeReleaseNULL(_systemService
);
818 if (!_systemService
->start(this)) {
819 DEBUG_ERROR("Unable to start the system service!\n");
820 _systemService
->detach(this);
821 OSSafeReleaseNULL(_systemService
);
827 if (_commonPartitionSize
) {
828 _commonService
= new IODTNVRAMVariables
;
830 if (!_commonService
|| !_commonService
->init(&gAppleNVRAMGuid
)) {
831 DEBUG_ERROR("Unable to start the common service!\n");
835 _commonService
->setName("options-common");
837 if (!_commonService
->attach(this)) {
838 DEBUG_ERROR("Unable to attach the common service!\n");
839 OSSafeReleaseNULL(_commonService
);
843 if (!_commonService
->start(this)) {
844 DEBUG_ERROR("Unable to start the common service!\n");
845 _commonService
->detach(this);
846 OSSafeReleaseNULL(_commonService
);
852 ret
= serializeVariables();
853 DEBUG_INFO("serializeVariables ret=0x%08x\n", ret
);
857 IODTNVRAM::initNVRAMImage(void)
859 char partitionID
[18];
860 UInt32 partitionOffset
, partitionLength
;
861 UInt32 currentLength
, currentOffset
= 0;
863 _commonPartitionOffset
= 0xFFFFFFFF;
864 _systemPartitionOffset
= 0xFFFFFFFF;
866 // Look through the partitions to find the OF and System partitions.
867 while (currentOffset
< _nvramSize
) {
868 bool common_partition
;
869 bool system_partition
;
871 chrp_nvram_header_t
* header
= (chrp_nvram_header_t
*)(_nvramImage
+ currentOffset
);
873 currentLength
= header
->len
* NVRAM_CHRP_LENGTH_BLOCK_SIZE
;
875 if (currentLength
< sizeof(chrp_nvram_header_t
)) {
879 partitionOffset
= currentOffset
+ sizeof(chrp_nvram_header_t
);
880 partitionLength
= currentLength
- sizeof(chrp_nvram_header_t
);
882 if ((partitionOffset
+ partitionLength
) > _nvramSize
) {
886 common_partition
= memcmp(header
->name
, NVRAM_CHRP_PARTITION_NAME_COMMON
, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON
)) == 0;
887 system_partition
= (memcmp(header
->name
, NVRAM_CHRP_PARTITION_NAME_SYSTEM
, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM
)) == 0) ||
888 (memcmp(header
->name
, NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY
, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY
)) == 0);
890 if (common_partition
) {
891 _commonPartitionOffset
= partitionOffset
;
892 _commonPartitionSize
= partitionLength
;
893 } else if (system_partition
) {
894 _systemPartitionOffset
= partitionOffset
;
895 _systemPartitionSize
= partitionLength
;
897 OSSharedPtr
<OSNumber
> partitionOffsetNumber
, partitionLengthNumber
;
899 // Construct the partition ID from the signature and name.
900 snprintf(partitionID
, sizeof(partitionID
), "0x%02x,", header
->sig
);
901 strncpy(partitionID
+ 5, header
->name
, sizeof(header
->name
));
902 partitionID
[17] = '\0';
904 partitionOffsetNumber
= OSNumber::withNumber(partitionOffset
, 32);
905 partitionLengthNumber
= OSNumber::withNumber(partitionLength
, 32);
907 // Save the partition offset and length
908 _nvramPartitionOffsets
->setObject(partitionID
, partitionOffsetNumber
.get());
909 _nvramPartitionLengths
->setObject(partitionID
, partitionLengthNumber
.get());
911 currentOffset
+= currentLength
;
914 if (_commonPartitionOffset
!= 0xFFFFFFFF) {
915 _commonImage
= _nvramImage
+ _commonPartitionOffset
;
918 if (_systemPartitionOffset
!= 0xFFFFFFFF) {
919 _systemImage
= _nvramImage
+ _systemPartitionOffset
;
922 DEBUG_ALWAYS("NVRAM : ofPartitionOffset - 0x%x, ofPartitionSize - 0x%x, systemPartitionOffset - 0x%x, systemPartitionSize - 0x%x\n",
923 (unsigned int) _commonPartitionOffset
, (unsigned int) _commonPartitionSize
, (unsigned int) _systemPartitionOffset
, (unsigned int) _systemPartitionSize
);
926 _freshInterval
= TRUE
; // we will allow sync() even before the first 15 minutes have passed.
932 IODTNVRAM::syncInternal(bool rateLimit
)
934 DEBUG_INFO("rateLimit=%d\n", rateLimit
);
936 // Don't try to perform controller operations if none has been registered.
937 if (_nvramController
== nullptr) {
941 // Rate limit requests to sync. Drivers that need this rate limiting will
942 // shadow the data and only write to flash when they get a sync call
943 if (rateLimit
&& !safeToSync()) {
947 DEBUG_INFO("Calling sync()\n");
950 _nvramController
->sync();
955 IODTNVRAM::sync(void)
961 IODTNVRAM::serializeProperties(OSSerialize
*s
) const
964 OSSharedPtr
<OSDictionary
> systemDict
, commonDict
, dict
;
965 OSSharedPtr
<OSCollectionIterator
> iter
;
967 unsigned int totalCapacity
= 0;
971 commonDict
= OSDictionary::withDictionary(_commonDict
.get());
975 systemDict
= OSDictionary::withDictionary(_systemDict
.get());
979 totalCapacity
+= (commonDict
!= nullptr) ? commonDict
->getCapacity() : 0;
980 totalCapacity
+= (systemDict
!= nullptr) ? systemDict
->getCapacity() : 0;
982 dict
= OSDictionary::withCapacity(totalCapacity
);
984 if (dict
== nullptr) {
985 DEBUG_ERROR("No dictionary\n");
989 // Copy system entries first if present then copy unique common entries
990 if (systemDict
!= nullptr) {
991 iter
= OSCollectionIterator::withCollection(systemDict
.get());
992 if (iter
== nullptr) {
993 DEBUG_ERROR("failed to create iterator\n");
997 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
998 if (verifyPermission(kIONVRAMOperationRead
, &gAppleSystemVariableGuid
, key
)) {
999 dict
->setObject(key
, systemDict
->getObject(key
));
1006 if (commonDict
!= nullptr) {
1007 iter
= OSCollectionIterator::withCollection(commonDict
.get());
1008 if (iter
== nullptr) {
1009 DEBUG_ERROR("failed to create common iterator\n");
1013 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
1014 if (dict
->getObject(key
) != nullptr) {
1018 if (verifyPermission(kIONVRAMOperationRead
, &gAppleNVRAMGuid
, key
)) {
1019 dict
->setObject(key
, commonDict
->getObject(key
));
1024 result
= dict
->serialize(s
);
1027 DEBUG_INFO("result=%d\n", result
);
1033 IODTNVRAM::chooseDictionary(IONVRAMOperation operation
, const uuid_t
*varGuid
, const char *variableName
, OSDictionary
**dict
) const
1035 if (_systemDict
!= nullptr) {
1036 bool systemGuid
= uuid_compare(*varGuid
, gAppleSystemVariableGuid
) == 0;
1038 if (variableInAllowList(variableName
)) {
1039 DEBUG_INFO("Using system dictionary due to allow list\n");
1041 DEBUG_ERROR("System GUID NOT used for %s\n", variableName
);
1043 *dict
= _systemDict
.get();
1044 } else if (systemGuid
) {
1045 DEBUG_INFO("Using system dictionary via GUID\n");
1046 *dict
= _systemDict
.get();
1048 DEBUG_INFO("Using common dictionary\n");
1049 *dict
= _commonDict
.get();
1052 DEBUG_INFO("Defaulting to common dictionary\n");
1053 *dict
= _commonDict
.get();
1056 return kIOReturnSuccess
;
1060 IODTNVRAM::handleSpecialVariables(const char *name
, uuid_t
*guid
, OSObject
*obj
, IOReturn
*error
)
1062 IOReturn err
= kIOReturnSuccess
;
1063 bool special
= false;
1067 if (strcmp(name
, "ResetNVRam") == 0) {
1068 DEBUG_INFO("%s requested\n", name
);
1070 if (uuid_compare(*guid
, gAppleSystemVariableGuid
) == 0) {
1071 if (_systemDict
!= nullptr) {
1072 _systemDict
->flushCollection();
1075 _commonDict
->flushCollection();
1076 DEBUG_INFO("system & common dictionary flushed\n");
1080 } else if (strcmp(name
, "ObliterateNVRam") == 0) {
1081 DEBUG_INFO("%s requested\n", name
);
1083 if ((_systemDict
!= nullptr) && (uuid_compare(*guid
, gAppleSystemVariableGuid
) == 0)) {
1084 const OSSymbol
*key
;
1085 OSSharedPtr
<OSDictionary
> newDict
;
1086 OSSharedPtr
<OSCollectionIterator
> iter
;
1088 newDict
= OSDictionary::withCapacity(_systemDict
->getCapacity());
1089 iter
= OSCollectionIterator::withCollection(newDict
.get());
1090 if ((newDict
== nullptr) || (iter
== nullptr)) {
1091 err
= kIOReturnNoMemory
;
1095 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
1096 const OSSymbol
*key
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
1097 if (key
== nullptr) {
1098 err
= kIOReturnNoMemory
;
1102 if (!verifyPermission(kIONVRAMOperationObliterate
, &gAppleSystemVariableGuid
, key
)) {
1103 newDict
->setObject(key
, _systemDict
->getObject(key
));
1107 _systemDict
= newDict
;
1109 DEBUG_INFO("system dictionary flushed\n");
1110 } else if (_commonDict
!= nullptr) {
1111 const OSSymbol
*key
;
1112 OSSharedPtr
<OSDictionary
> newDict
;
1113 OSSharedPtr
<OSCollectionIterator
> iter
;
1115 newDict
= OSDictionary::withCapacity(_commonDict
->getCapacity());
1116 iter
= OSCollectionIterator::withCollection(newDict
.get());
1117 if ((newDict
== nullptr) || (iter
== nullptr)) {
1118 err
= kIOReturnNoMemory
;
1122 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
1123 if (!verifyPermission(kIONVRAMOperationObliterate
, &gAppleNVRAMGuid
, key
)) {
1124 newDict
->setObject(key
, _commonDict
->getObject(key
));
1128 _commonDict
= newDict
;
1130 DEBUG_INFO("common dictionary flushed\n");
1144 OSSharedPtr
<OSObject
>
1145 IODTNVRAM::copyProperty(const OSSymbol
*aKey
) const
1148 const char *variableName
;
1151 OSSharedPtr
<OSObject
> theObject
= nullptr;
1153 if (aKey
->isEqualTo(kIOBSDNameKey
) ||
1154 aKey
->isEqualTo(kIOBSDNamesKey
) ||
1155 aKey
->isEqualTo(kIOBSDMajorKey
) ||
1156 aKey
->isEqualTo(kIOBSDMinorKey
) ||
1157 aKey
->isEqualTo(kIOBSDUnitKey
)) {
1158 // These will never match.
1159 // Check here and exit to avoid logging spam
1162 DEBUG_INFO("aKey=%s\n", aKey
->getCStringNoCopy());
1164 parseVariableName(aKey
->getCStringNoCopy(), &varGuid
, &variableName
);
1166 result
= chooseDictionary(kIONVRAMOperationRead
, &varGuid
, variableName
, &dict
);
1167 if (result
!= kIOReturnSuccess
) {
1171 if (!verifyPermission(kIONVRAMOperationRead
, &varGuid
, variableName
)) {
1172 DEBUG_INFO("Not privileged\n");
1177 theObject
.reset(dict
->getObject(variableName
), OSRetain
);
1180 if (theObject
!= nullptr) {
1181 DEBUG_INFO("found data\n");
1188 OSSharedPtr
<OSObject
>
1189 IODTNVRAM::copyProperty(const char *aKey
) const
1191 OSSharedPtr
<const OSSymbol
> keySymbol
;
1192 OSSharedPtr
<OSObject
> theObject
;
1194 keySymbol
= OSSymbol::withCString(aKey
);
1195 if (keySymbol
!= nullptr) {
1196 theObject
= copyProperty(keySymbol
.get());
1203 IODTNVRAM::getProperty(const OSSymbol
*aKey
) const
1205 // The shared pointer gets released at the end of the function,
1206 // and returns a view into theObject.
1207 OSSharedPtr
<OSObject
> theObject
= copyProperty(aKey
);
1209 return theObject
.get();
1213 IODTNVRAM::getProperty(const char *aKey
) const
1215 // The shared pointer gets released at the end of the function,
1216 // and returns a view into theObject.
1217 OSSharedPtr
<OSObject
> theObject
= copyProperty(aKey
);
1219 return theObject
.get();
1223 IODTNVRAM::setPropertyInternal(const OSSymbol
*aKey
, OSObject
*anObject
)
1225 IOReturn result
= kIOReturnSuccess
;
1226 bool remove
= false;
1227 OSString
*tmpString
= nullptr;
1228 OSSharedPtr
<OSObject
> propObject
, oldObject
;
1229 OSSharedPtr
<OSObject
> sharedObject(anObject
, OSRetain
);
1230 const char *variableName
;
1233 bool deletePropertyKey
, syncNowPropertyKey
, forceSyncNowPropertyKey
;
1235 size_t propDataSize
= 0;
1237 DEBUG_INFO("aKey=%s\n", aKey
->getCStringNoCopy());
1239 parseVariableName(aKey
->getCStringNoCopy(), &varGuid
, &variableName
);
1240 deletePropertyKey
= strncmp(variableName
, kIONVRAMDeletePropertyKey
, sizeof(kIONVRAMDeletePropertyKey
)) == 0;
1241 syncNowPropertyKey
= strncmp(variableName
, kIONVRAMSyncNowPropertyKey
, sizeof(kIONVRAMSyncNowPropertyKey
)) == 0;
1242 forceSyncNowPropertyKey
= strncmp(variableName
, kIONVRAMForceSyncNowPropertyKey
, sizeof(kIONVRAMForceSyncNowPropertyKey
)) == 0;
1244 if (deletePropertyKey
) {
1245 tmpString
= OSDynamicCast(OSString
, anObject
);
1246 if (tmpString
!= nullptr) {
1247 DEBUG_INFO("kIONVRAMDeletePropertyKey found\n");
1248 OSSharedPtr
<const OSSymbol
> sharedKey
= OSSymbol::withString(tmpString
);
1249 removeProperty(sharedKey
.get());
1251 DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1252 result
= kIOReturnError
;
1255 } else if (syncNowPropertyKey
|| forceSyncNowPropertyKey
) {
1256 tmpString
= OSDynamicCast(OSString
, anObject
);
1257 DEBUG_INFO("NVRAM sync key %s found\n", aKey
->getCStringNoCopy());
1258 if (tmpString
!= nullptr) {
1259 // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1260 syncInternal(syncNowPropertyKey
);
1262 DEBUG_INFO("%s value needs to be an OSString\n", variableName
);
1263 result
= kIOReturnError
;
1268 result
= chooseDictionary(kIONVRAMOperationWrite
, &varGuid
, variableName
, &dict
);
1269 if (result
!= kIOReturnSuccess
) {
1273 if (!verifyPermission(kIONVRAMOperationWrite
, &varGuid
, variableName
)) {
1274 DEBUG_INFO("Not privileged\n");
1275 result
= kIOReturnNotPrivileged
;
1279 // Make sure the object is of the correct type.
1280 switch (getVariableType(variableName
)) {
1281 case kOFVariableTypeBoolean
:
1282 propObject
= OSDynamicPtrCast
<OSBoolean
>(sharedObject
);
1285 case kOFVariableTypeNumber
:
1286 propObject
= OSDynamicPtrCast
<OSNumber
>(sharedObject
);
1289 case kOFVariableTypeString
:
1290 propObject
= OSDynamicPtrCast
<OSString
>(sharedObject
);
1291 if (propObject
!= nullptr) {
1292 propDataSize
= (OSDynamicPtrCast
<OSString
>(propObject
))->getLength();
1294 if (aKey
->isEqualTo(kIONVRAMBootArgsKey
) && (propDataSize
>= BOOT_LINE_LENGTH
)) {
1295 DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize
);
1296 result
= kIOReturnNoSpace
;
1302 case kOFVariableTypeData
:
1303 propObject
= OSDynamicPtrCast
<OSData
>(sharedObject
);
1304 if (propObject
== nullptr) {
1305 tmpString
= OSDynamicCast(OSString
, sharedObject
.get());
1306 if (tmpString
!= nullptr) {
1307 propObject
= OSData::withBytes(tmpString
->getCStringNoCopy(),
1308 tmpString
->getLength());
1312 if (propObject
!= nullptr) {
1313 propDataSize
= (OSDynamicPtrCast
<OSData
>(propObject
))->getLength();
1316 #if defined(XNU_TARGET_OS_OSX)
1317 if ((propObject
!= nullptr) && ((OSDynamicPtrCast
<OSData
>(propObject
))->getLength() == 0)) {
1320 #endif /* defined(XNU_TARGET_OS_OSX) */
1326 if (propObject
== nullptr) {
1327 DEBUG_INFO("No property object\n");
1328 result
= kIOReturnBadArgument
;
1332 if (!verifyWriteSizeLimit(&varGuid
, variableName
, propDataSize
)) {
1333 DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize
, variableName
);
1334 result
= kIOReturnNoSpace
;
1339 ok
= handleSpecialVariables(variableName
, &varGuid
, propObject
.get(), &result
);
1343 serializeVariables();
1348 oldObject
.reset(dict
->getObject(variableName
), OSRetain
);
1349 if (remove
== false) {
1350 DEBUG_INFO("Adding object\n");
1351 if (!dict
->setObject(variableName
, propObject
.get())) {
1352 result
= kIOReturnBadArgument
;
1355 DEBUG_INFO("Removing object\n");
1356 // Check for existence so we can decide whether we need to sync variables
1358 result
= removePropertyInternal(aKey
);
1360 result
= kIOReturnNotFound
;
1365 if (result
== kIOReturnSuccess
) {
1366 result
= serializeVariables();
1367 if (result
!= kIOReturnSuccess
) {
1368 DEBUG_ERROR("serializeVariables failed, result=0x%08x\n", result
);
1372 dict
->setObject(variableName
, oldObject
.get());
1374 dict
->removeObject(variableName
);
1378 (void) serializeVariables();
1379 result
= kIOReturnNoMemory
;
1391 DEBUG_INFO("result=0x%08x\n", result
);
1397 IODTNVRAM::setProperty(const OSSymbol
*aKey
, OSObject
*anObject
)
1399 return setPropertyInternal(aKey
, anObject
) == kIOReturnSuccess
;
1403 IODTNVRAM::removeProperty(const OSSymbol
*aKey
)
1408 ret
= removePropertyInternal(aKey
);
1411 if (ret
== kIOReturnSuccess
) {
1412 serializeVariables();
1414 DEBUG_INFO("removePropertyInternal failed, ret=0x%08x\n", ret
);
1419 IODTNVRAM::removePropertyInternal(const OSSymbol
*aKey
)
1422 const char *variableName
;
1426 DEBUG_INFO("aKey=%s\n", aKey
->getCStringNoCopy());
1430 parseVariableName(aKey
->getCStringNoCopy(), &varGuid
, &variableName
);
1432 result
= chooseDictionary(kIONVRAMOperationDelete
, &varGuid
, variableName
, &dict
);
1433 if (result
!= kIOReturnSuccess
) {
1437 if (!verifyPermission(kIONVRAMOperationDelete
, &varGuid
, variableName
)) {
1438 DEBUG_INFO("Not priveleged\n");
1439 result
= kIOReturnNotPrivileged
;
1443 // If the object exists, remove it from the dictionary.
1444 if (dict
->getObject(variableName
) != nullptr) {
1445 dict
->removeObject(variableName
);
1453 IODTNVRAM::setProperties(OSObject
*properties
)
1455 IOReturn result
= kIOReturnSuccess
;
1457 const OSSymbol
*key
;
1459 OSSharedPtr
<OSCollectionIterator
> iter
;
1461 dict
= OSDynamicCast(OSDictionary
, properties
);
1462 if (dict
== nullptr) {
1463 DEBUG_ERROR("Not a dictionary\n");
1464 return kIOReturnBadArgument
;
1467 iter
= OSCollectionIterator::withCollection(dict
);
1468 if (iter
== nullptr) {
1469 DEBUG_ERROR("Couldn't create iterator\n");
1470 return kIOReturnBadArgument
;
1473 while (result
== kIOReturnSuccess
) {
1474 key
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
1475 if (key
== nullptr) {
1479 object
= dict
->getObject(key
);
1480 if (object
== nullptr) {
1484 result
= setPropertyInternal(key
, object
);
1487 DEBUG_INFO("result=0x%08x\n", result
);
1493 IODTNVRAM::readXPRAM(IOByteCount offset
, UInt8
*buffer
,
1496 return kIOReturnUnsupported
;
1500 IODTNVRAM::writeXPRAM(IOByteCount offset
, UInt8
*buffer
,
1503 return kIOReturnUnsupported
;
1507 IODTNVRAM::readNVRAMProperty(IORegistryEntry
*entry
,
1508 const OSSymbol
**name
,
1513 err
= readNVRAMPropertyType1(entry
, name
, value
);
1519 IODTNVRAM::writeNVRAMProperty(IORegistryEntry
*entry
,
1520 const OSSymbol
*name
,
1525 err
= writeNVRAMPropertyType1(entry
, name
, value
);
1531 IODTNVRAM::getNVRAMPartitions(void)
1533 return _nvramPartitionLengths
.get();
1537 IODTNVRAM::readNVRAMPartition(const OSSymbol
*partitionID
,
1538 IOByteCount offset
, UInt8
*buffer
,
1541 OSNumber
*partitionOffsetNumber
, *partitionLengthNumber
;
1542 UInt32 partitionOffset
, partitionLength
, end
;
1544 partitionOffsetNumber
=
1545 (OSNumber
*)_nvramPartitionOffsets
->getObject(partitionID
);
1546 partitionLengthNumber
=
1547 (OSNumber
*)_nvramPartitionLengths
->getObject(partitionID
);
1549 if ((partitionOffsetNumber
== nullptr) || (partitionLengthNumber
== nullptr)) {
1550 return kIOReturnNotFound
;
1553 partitionOffset
= partitionOffsetNumber
->unsigned32BitValue();
1554 partitionLength
= partitionLengthNumber
->unsigned32BitValue();
1556 if (os_add_overflow(offset
, length
, &end
)) {
1557 return kIOReturnBadArgument
;
1559 if ((buffer
== nullptr) || (length
== 0) || (end
> partitionLength
)) {
1560 return kIOReturnBadArgument
;
1563 bcopy(_nvramImage
+ partitionOffset
+ offset
, buffer
, length
);
1565 return kIOReturnSuccess
;
1569 IODTNVRAM::writeNVRAMPartition(const OSSymbol
*partitionID
,
1570 IOByteCount offset
, UInt8
*buffer
,
1573 OSNumber
*partitionOffsetNumber
, *partitionLengthNumber
;
1574 UInt32 partitionOffset
, partitionLength
, end
;
1576 partitionOffsetNumber
=
1577 (OSNumber
*)_nvramPartitionOffsets
->getObject(partitionID
);
1578 partitionLengthNumber
=
1579 (OSNumber
*)_nvramPartitionLengths
->getObject(partitionID
);
1581 if ((partitionOffsetNumber
== nullptr) || (partitionLengthNumber
== nullptr)) {
1582 return kIOReturnNotFound
;
1585 partitionOffset
= partitionOffsetNumber
->unsigned32BitValue();
1586 partitionLength
= partitionLengthNumber
->unsigned32BitValue();
1588 if (os_add_overflow(offset
, length
, &end
)) {
1589 return kIOReturnBadArgument
;
1591 if ((buffer
== nullptr) || (length
== 0) || (end
> partitionLength
)) {
1592 return kIOReturnBadArgument
;
1595 bcopy(buffer
, _nvramImage
+ partitionOffset
+ offset
, length
);
1597 if (_nvramController
!= nullptr) {
1598 _nvramController
->write(0, _nvramImage
, _nvramSize
);
1601 return kIOReturnSuccess
;
1605 IODTNVRAM::savePanicInfo(UInt8
*buffer
, IOByteCount length
)
1613 IODTNVRAM::calculatePartitionChecksum(UInt8
*partitionHeader
)
1615 UInt8 cnt
, isum
, csum
= 0;
1617 for (cnt
= 0; cnt
< 0x10; cnt
++) {
1618 isum
= csum
+ partitionHeader
[cnt
];
1629 IODTNVRAM::initVariables(void)
1632 UInt8
*propName
, *propData
;
1633 UInt32 propNameLength
, propDataLength
, regionIndex
;
1634 OSSharedPtr
<const OSSymbol
> propSymbol
;
1635 OSSharedPtr
<OSObject
> propObject
;
1636 NVRAMRegionInfo
*currentRegion
;
1637 NVRAMRegionInfo variableRegions
[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON
, _commonPartitionOffset
, _commonPartitionSize
, _commonDict
, _commonImage
},
1638 { NVRAM_CHRP_PARTITION_NAME_SYSTEM
, _systemPartitionOffset
, _systemPartitionSize
, _systemDict
, _systemImage
} };
1640 DEBUG_INFO("...\n");
1642 for (regionIndex
= 0; regionIndex
< ARRAY_SIZE(variableRegions
); regionIndex
++) {
1643 currentRegion
= &variableRegions
[regionIndex
];
1645 if (currentRegion
->size
== 0) {
1649 currentRegion
->dict
= OSDictionary::withCapacity(1);
1651 DEBUG_INFO("region = %s\n", currentRegion
->name
);
1653 while (cnt
< currentRegion
->size
) {
1654 // Break if there is no name.
1655 if (currentRegion
->image
[cnt
] == '\0') {
1659 // Find the length of the name.
1660 propName
= currentRegion
->image
+ cnt
;
1661 for (propNameLength
= 0; (cnt
+ propNameLength
) < currentRegion
->size
;
1663 if (currentRegion
->image
[cnt
+ propNameLength
] == '=') {
1668 // Break if the name goes past the end of the partition.
1669 if ((cnt
+ propNameLength
) >= currentRegion
->size
) {
1672 cnt
+= propNameLength
+ 1;
1674 propData
= currentRegion
->image
+ cnt
;
1675 for (propDataLength
= 0; (cnt
+ propDataLength
) < currentRegion
->size
;
1677 if (currentRegion
->image
[cnt
+ propDataLength
] == '\0') {
1682 // Break if the data goes past the end of the partition.
1683 if ((cnt
+ propDataLength
) >= currentRegion
->size
) {
1686 cnt
+= propDataLength
+ 1;
1688 if (convertPropToObject(propName
, propNameLength
,
1689 propData
, propDataLength
,
1690 propSymbol
, propObject
)) {
1691 DEBUG_INFO("adding %s, dataLength=%u\n", propSymbol
.get()->getCStringNoCopy(), (unsigned int)propDataLength
);
1692 currentRegion
->dict
.get()->setObject(propSymbol
.get(), propObject
.get());
1697 // Create the boot-args property if it is not in the dictionary.
1698 if (_commonDict
->getObject(kIONVRAMBootArgsKey
) == nullptr) {
1699 propObject
= OSString::withCStringNoCopy("");
1700 if (propObject
!= nullptr) {
1701 _commonDict
->setObject(kIONVRAMBootArgsKey
, propObject
.get());
1705 DEBUG_INFO("%s _commonDict=%p _systemDict=%p\n", __FUNCTION__
, _commonDict
.get(), _systemDict
.get());
1707 return kIOReturnSuccess
;
1711 IODTNVRAM::syncOFVariables(void)
1713 return kIOReturnUnsupported
;
1717 IODTNVRAM::serializeVariables(void)
1721 UInt32 length
, maxLength
, regionIndex
;
1722 UInt8
*buffer
, *tmpBuffer
;
1723 const OSSymbol
*tmpSymbol
;
1724 OSObject
*tmpObject
;
1725 OSSharedPtr
<OSCollectionIterator
> iter
;
1726 OSSharedPtr
<OSNumber
> sizeUsed
;
1727 UInt32 systemUsed
= 0;
1728 UInt32 commonUsed
= 0;
1729 OSSharedPtr
<OSData
> nvramImage
;
1730 NVRAMRegionInfo
*currentRegion
;
1731 NVRAMRegionInfo variableRegions
[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON
, _commonPartitionOffset
, _commonPartitionSize
, _commonDict
, _commonImage
},
1732 { NVRAM_CHRP_PARTITION_NAME_SYSTEM
, _systemPartitionOffset
, _systemPartitionSize
, _systemDict
, _systemImage
} };
1734 if (_systemPanicked
) {
1735 return kIOReturnNotReady
;
1738 if (_nvramController
== nullptr) {
1739 DEBUG_ERROR("No _nvramController\n");
1740 return kIOReturnNotReady
;
1743 DEBUG_INFO("...\n");
1747 for (regionIndex
= 0; regionIndex
< ARRAY_SIZE(variableRegions
); regionIndex
++) {
1748 currentRegion
= &variableRegions
[regionIndex
];
1750 if (currentRegion
->size
== 0) {
1754 DEBUG_INFO("region = %s\n", currentRegion
->name
);
1755 buffer
= tmpBuffer
= IONew(UInt8
, currentRegion
->size
);
1756 if (buffer
== nullptr) {
1757 return kIOReturnNoMemory
;
1759 bzero(buffer
, currentRegion
->size
);
1762 maxLength
= currentRegion
->size
;
1764 iter
= OSCollectionIterator::withCollection(currentRegion
->dict
.get());
1765 if (iter
== nullptr) {
1770 tmpSymbol
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
1771 if (tmpSymbol
== nullptr) {
1775 DEBUG_INFO("adding variable %s\n", tmpSymbol
->getCStringNoCopy());
1777 tmpObject
= currentRegion
->dict
->getObject(tmpSymbol
);
1780 ok
= convertObjectToProp(tmpBuffer
, &length
, tmpSymbol
, tmpObject
);
1782 tmpBuffer
+= length
;
1783 maxLength
-= length
;
1788 bcopy(buffer
, currentRegion
->image
, currentRegion
->size
);
1791 IODelete(buffer
, UInt8
, currentRegion
->size
);
1793 if ((strncmp(currentRegion
->name
, NVRAM_CHRP_PARTITION_NAME_SYSTEM
, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM
)) == 0) &&
1794 (_systemService
!= nullptr)) {
1795 _systemService
->setProperties(_systemDict
.get());
1796 systemUsed
= maxLength
;
1797 } else if ((strncmp(currentRegion
->name
, NVRAM_CHRP_PARTITION_NAME_COMMON
, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON
)) == 0) &&
1798 (_commonService
!= nullptr)) {
1799 _commonService
->setProperties(_commonDict
.get());
1800 commonUsed
= maxLength
;
1804 return kIOReturnBadArgument
;
1808 nvramImage
= OSData::withBytes(_nvramImage
, _nvramSize
);
1812 DEBUG_INFO("ok=%d\n", ok
);
1816 if (_systemService
) {
1817 sizeUsed
= OSNumber::withNumber(systemUsed
, 32);
1818 _nvramController
->setProperty("SystemUsed", sizeUsed
.get());
1822 if (_commonService
) {
1823 sizeUsed
= OSNumber::withNumber(commonUsed
, 32);
1824 _nvramController
->setProperty("CommonUsed", sizeUsed
.get());
1828 ret
= _nvramController
->write(0, (uint8_t *)nvramImage
->getBytesNoCopy(), nvramImage
->getLength());
1836 IODTNVRAM::getOFVariableType(const char *propName
) const
1842 IODTNVRAM::getOFVariableType(const OSSymbol
*propSymbol
) const
1849 IODTNVRAM::getOFVariablePerm(const char *propName
) const
1855 IODTNVRAM::getOFVariablePerm(const OSSymbol
*propSymbol
) const
1861 IODTNVRAM::getOWVariableInfo(UInt32 variableNumber
, const OSSymbol
**propSymbol
,
1862 UInt32
*propType
, UInt32
*propOffset
)
1868 IODTNVRAM::convertPropToObject(UInt8
*propName
, UInt32 propNameLength
,
1869 UInt8
*propData
, UInt32 propDataLength
,
1870 const OSSymbol
**propSymbol
,
1871 OSObject
**propObject
)
1873 OSSharedPtr
<const OSSymbol
> tmpSymbol
;
1874 OSSharedPtr
<OSNumber
> tmpNumber
;
1875 OSSharedPtr
<OSString
> tmpString
;
1876 OSSharedPtr
<OSObject
> tmpObject
= nullptr;
1878 propName
[propNameLength
] = '\0';
1879 tmpSymbol
= OSSymbol::withCString((const char *)propName
);
1880 propName
[propNameLength
] = '=';
1881 if (tmpSymbol
== nullptr) {
1885 switch (getVariableType(tmpSymbol
.get())) {
1886 case kOFVariableTypeBoolean
:
1887 if (!strncmp("true", (const char *)propData
, propDataLength
)) {
1888 tmpObject
.reset(kOSBooleanTrue
, OSRetain
);
1889 } else if (!strncmp("false", (const char *)propData
, propDataLength
)) {
1890 tmpObject
.reset(kOSBooleanFalse
, OSRetain
);
1894 case kOFVariableTypeNumber
:
1895 tmpNumber
= OSNumber::withNumber(strtol((const char *)propData
, nullptr, 0), 32);
1896 if (tmpNumber
!= nullptr) {
1897 tmpObject
= tmpNumber
;
1901 case kOFVariableTypeString
:
1902 tmpString
= OSString::withCString((const char *)propData
);
1903 if (tmpString
!= nullptr) {
1904 tmpObject
= tmpString
;
1908 case kOFVariableTypeData
:
1909 tmpObject
= unescapeBytesToData(propData
, propDataLength
);
1916 if (tmpObject
== nullptr) {
1921 *propSymbol
= tmpSymbol
.detach();
1922 *propObject
= tmpObject
.detach();
1928 IODTNVRAM::convertPropToObject(UInt8
*propName
, UInt32 propNameLength
,
1929 UInt8
*propData
, UInt32 propDataLength
,
1930 OSSharedPtr
<const OSSymbol
>& propSymbol
,
1931 OSSharedPtr
<OSObject
>& propObject
)
1933 const OSSymbol
* propSymbolRaw
= nullptr;
1934 OSObject
* propObjectRaw
= nullptr;
1935 bool result
= convertPropToObject(propName
, propNameLength
, propData
, propDataLength
,
1936 &propSymbolRaw
, &propObjectRaw
);
1937 propSymbol
.reset(propSymbolRaw
, OSNoRetain
);
1938 propObject
.reset(propObjectRaw
, OSNoRetain
);
1943 IODTNVRAM::convertObjectToProp(UInt8
*buffer
, UInt32
*length
,
1944 const OSSymbol
*propSymbol
, OSObject
*propObject
)
1946 const UInt8
*propName
;
1947 UInt32 propNameLength
, propDataLength
, remaining
;
1948 IONVRAMVariableType propType
;
1949 OSBoolean
*tmpBoolean
= nullptr;
1950 OSNumber
*tmpNumber
= nullptr;
1951 OSString
*tmpString
= nullptr;
1952 OSSharedPtr
<OSData
> tmpData
;
1954 propName
= (const UInt8
*)propSymbol
->getCStringNoCopy();
1955 propNameLength
= propSymbol
->getLength();
1956 propType
= getVariableType(propSymbol
);
1958 // Get the size of the data.
1959 propDataLength
= 0xFFFFFFFF;
1961 case kOFVariableTypeBoolean
:
1962 tmpBoolean
= OSDynamicCast(OSBoolean
, propObject
);
1963 if (tmpBoolean
!= nullptr) {
1968 case kOFVariableTypeNumber
:
1969 tmpNumber
= OSDynamicCast(OSNumber
, propObject
);
1970 if (tmpNumber
!= nullptr) {
1971 propDataLength
= 10;
1975 case kOFVariableTypeString
:
1976 tmpString
= OSDynamicCast(OSString
, propObject
);
1977 if (tmpString
!= nullptr) {
1978 propDataLength
= tmpString
->getLength();
1982 case kOFVariableTypeData
:
1983 tmpData
.reset(OSDynamicCast(OSData
, propObject
), OSNoRetain
);
1984 if (tmpData
!= nullptr) {
1985 tmpData
= escapeDataToData(tmpData
.detach());
1986 propDataLength
= tmpData
->getLength();
1994 // Make sure the propertySize is known and will fit.
1995 if (propDataLength
== 0xFFFFFFFF) {
1998 if ((propNameLength
+ propDataLength
+ 2) > *length
) {
2002 // Copy the property name equal sign.
2003 buffer
+= snprintf((char *)buffer
, *length
, "%s=", propName
);
2004 remaining
= *length
- propNameLength
- 1;
2007 case kOFVariableTypeBoolean
:
2008 if (tmpBoolean
->getValue()) {
2009 strlcpy((char *)buffer
, "true", remaining
);
2011 strlcpy((char *)buffer
, "false", remaining
);
2015 case kOFVariableTypeNumber
:
2017 uint32_t tmpValue
= tmpNumber
->unsigned32BitValue();
2018 if (tmpValue
== 0xFFFFFFFF) {
2019 strlcpy((char *)buffer
, "-1", remaining
);
2020 } else if (tmpValue
< 1000) {
2021 snprintf((char *)buffer
, remaining
, "%d", (uint32_t)tmpValue
);
2023 snprintf((char *)buffer
, remaining
, "0x%x", (uint32_t)tmpValue
);
2028 case kOFVariableTypeString
:
2029 strlcpy((char *)buffer
, tmpString
->getCStringNoCopy(), remaining
);
2032 case kOFVariableTypeData
:
2033 bcopy(tmpData
->getBytesNoCopy(), buffer
, propDataLength
);
2041 propDataLength
= ((UInt32
) strlen((const char *)buffer
));
2043 *length
= propNameLength
+ propDataLength
+ 2;
2050 IODTNVRAM::generateOWChecksum(UInt8
*buffer
)
2052 UInt32 cnt
, checksum
= 0;
2053 UInt16
*tmpBuffer
= (UInt16
*)buffer
;
2055 for (cnt
= 0; cnt
< _commonPartitionSize
/ 2; cnt
++) {
2056 checksum
+= tmpBuffer
[cnt
];
2059 return checksum
% 0x0000FFFF;
2063 IODTNVRAM::validateOWChecksum(UInt8
*buffer
)
2065 UInt32 cnt
, checksum
, sum
= 0;
2066 UInt16
*tmpBuffer
= (UInt16
*)buffer
;
2068 for (cnt
= 0; cnt
< _commonPartitionSize
/ 2; cnt
++) {
2069 sum
+= tmpBuffer
[cnt
];
2072 checksum
= (sum
>> 16) + (sum
& 0x0000FFFF);
2073 if (checksum
== 0x10000) {
2076 checksum
= (checksum
^ 0x0000FFFF) & 0x0000FFFF;
2078 return checksum
== 0;
2082 IODTNVRAM::updateOWBootArgs(const OSSymbol
*key
, OSObject
*value
)
2088 IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor
*hdr
, UInt32
*where
)
2094 IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry
*entry
,
2095 const OSSymbol
**name
,
2098 return kIOReturnUnsupported
;
2103 IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry
*entry
,
2104 const OSSymbol
*name
,
2107 return kIOReturnUnsupported
;
2112 IODTNVRAM::unescapeBytesToData(const UInt8
*bytes
, UInt32 length
)
2114 OSSharedPtr
<OSData
> data
;
2115 UInt32 totalLength
= 0;
2120 // Calculate the actual length of the data.
2123 for (cnt
= 0; cnt
< length
;) {
2124 byte
= bytes
[cnt
++];
2126 byte
= bytes
[cnt
++];
2135 totalLength
+= cnt2
;
2139 // Create an empty OSData of the correct size.
2140 data
= OSData::withCapacity(totalLength
);
2141 if (data
!= nullptr) {
2142 for (cnt
= 0; cnt
< length
;) {
2143 byte
= bytes
[cnt
++];
2145 byte
= bytes
[cnt
++];
2147 byte
= (byte
& 0x80) ? 0xFF : 0x00;
2151 data
->appendByte(byte
, cnt2
);
2160 IODTNVRAM::escapeDataToData(OSData
* value
)
2162 OSSharedPtr
<OSData
> result
;
2163 const UInt8
*startPtr
;
2164 const UInt8
*endPtr
;
2165 const UInt8
*wherePtr
;
2169 wherePtr
= (const UInt8
*) value
->getBytesNoCopy();
2170 endPtr
= wherePtr
+ value
->getLength();
2172 result
= OSData::withCapacity((unsigned int) (endPtr
- wherePtr
));
2177 while (wherePtr
< endPtr
) {
2178 startPtr
= wherePtr
;
2180 if ((byte
== 0x00) || (byte
== 0xFF)) {
2182 ((wherePtr
- startPtr
) < 0x80) && (wherePtr
< endPtr
) && (byte
== *wherePtr
);
2185 ok
&= result
->appendByte(0xff, 1);
2186 byte
= (byte
& 0x80) | ((UInt8
)(wherePtr
- startPtr
));
2188 ok
&= result
->appendByte(byte
, 1);
2190 ok
&= result
->appendByte(0, 1);
2200 IsApplePropertyName(const char * propName
)
2203 while ((c
= *propName
++)) {
2204 if ((c
>= 'A') && (c
<= 'Z')) {
2213 IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry
*entry
,
2214 const OSSymbol
**name
,
2217 IOReturn err
= kIOReturnNoResources
;
2219 const UInt8
*startPtr
;
2220 const UInt8
*endPtr
;
2221 const UInt8
*wherePtr
;
2222 const UInt8
*nvPath
= nullptr;
2223 const char *nvName
= nullptr;
2224 const char *resultName
= nullptr;
2225 const UInt8
*resultValue
= nullptr;
2226 UInt32 resultValueLen
= 0;
2230 data
= OSDynamicCast(OSData
, _commonDict
->getObject(_registryPropertiesKey
.get()));
2233 if (data
== nullptr) {
2237 startPtr
= (const UInt8
*) data
->getBytesNoCopy();
2238 endPtr
= startPtr
+ data
->getLength();
2240 wherePtr
= startPtr
;
2241 while (wherePtr
< endPtr
) {
2242 byte
= *(wherePtr
++);
2247 if (nvPath
== nullptr) {
2249 } else if (nvName
== nullptr) {
2250 nvName
= (const char *) startPtr
;
2252 OSSharedPtr
<IORegistryEntry
> compareEntry
= IORegistryEntry::fromPath((const char *) nvPath
, gIODTPlane
);
2253 if (entry
== compareEntry
) {
2254 bool appleProp
= IsApplePropertyName(nvName
);
2255 if (!appleProp
|| !resultName
) {
2256 resultName
= nvName
;
2257 resultValue
= startPtr
;
2258 resultValueLen
= (UInt32
) (wherePtr
- startPtr
- 1); // OSData getLength() is 32b
2267 startPtr
= wherePtr
;
2270 *name
= OSSymbol::withCString(resultName
).detach();
2271 *value
= unescapeBytesToData(resultValue
, resultValueLen
).detach();
2272 if ((*name
!= nullptr) && (*value
!= nullptr)) {
2273 err
= kIOReturnSuccess
;
2275 err
= kIOReturnNoMemory
;
2282 IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry
*entry
,
2283 const OSSymbol
*propName
,
2286 OSSharedPtr
<OSData
> data
, oldData
;
2287 const UInt8
*startPtr
;
2288 const UInt8
*propStart
;
2289 const UInt8
*endPtr
;
2290 const UInt8
*wherePtr
;
2291 const UInt8
*nvPath
= nullptr;
2292 const char *nvName
= nullptr;
2297 bool settingAppleProp
;
2299 settingAppleProp
= IsApplePropertyName(propName
->getCStringNoCopy());
2301 // copy over existing properties for other entries
2305 oldData
.reset(OSDynamicCast(OSData
, _commonDict
->getObject(_registryPropertiesKey
.get())), OSRetain
);
2307 startPtr
= (const UInt8
*) oldData
->getBytesNoCopy();
2308 endPtr
= startPtr
+ oldData
->getLength();
2310 propStart
= startPtr
;
2311 wherePtr
= startPtr
;
2312 while (wherePtr
< endPtr
) {
2313 byte
= *(wherePtr
++);
2317 if (nvPath
== nullptr) {
2319 } else if (nvName
== nullptr) {
2320 nvName
= (const char *) startPtr
;
2322 OSSharedPtr
<IORegistryEntry
> compareEntry
= IORegistryEntry::fromPath((const char *) nvPath
, gIODTPlane
);
2324 if (entry
== compareEntry
) {
2325 if ((settingAppleProp
&& propName
->isEqualTo(nvName
))
2326 || (!settingAppleProp
&& !IsApplePropertyName(nvName
))) {
2327 // delete old property (nvPath -> wherePtr) source OSData len is 32b
2328 data
= OSData::withBytes(propStart
, (UInt32
)(nvPath
- propStart
));
2330 ok
&= data
->appendBytes(wherePtr
, (UInt32
)(endPtr
- wherePtr
));
2339 startPtr
= wherePtr
;
2343 // make the new property
2347 data
= OSData::withData(oldData
.get());
2349 data
= OSData::withCapacity(16);
2356 if (ok
&& value
&& value
->getLength()) {
2358 // get entries in path
2359 OSSharedPtr
<OSArray
> array
= OSArray::withCapacity(5);
2365 array
->setObject(entry
);
2366 } while ((entry
= entry
->getParentEntry(gIODTPlane
)));
2369 for (int i
= array
->getCount() - 3;
2370 (entry
= (IORegistryEntry
*) array
->getObject(i
));
2372 name
= entry
->getName(gIODTPlane
);
2373 comp
= entry
->getLocation(gIODTPlane
);
2375 ok
&= data
->appendBytes("/@", 2);
2380 ok
&= data
->appendByte('/', 1);
2383 ok
&= data
->appendBytes(comp
, (unsigned int) strnlen(comp
, UINT16_MAX
));
2385 ok
&= data
->appendByte(0, 1);
2387 ok
&= data
->appendBytes(propName
->getCStringNoCopy(), propName
->getLength() + 1);
2389 // append escaped data
2390 OSSharedPtr
<OSData
> escapedData
= escapeDataToData(value
);
2391 ok
&= (escapedData
!= nullptr);
2393 ok
&= data
->appendBytes(escapedData
.get());
2399 ok
= _commonDict
->setObject(_registryPropertiesKey
.get(), data
.get());
2405 if (serializeVariables() != kIOReturnSuccess
) {
2408 _commonDict
->setObject(_registryPropertiesKey
.get(), oldData
.get());
2410 _commonDict
->removeObject(_registryPropertiesKey
.get());
2414 (void) serializeVariables();
2421 return ok
? kIOReturnSuccess
: kIOReturnNoMemory
;
2425 IODTNVRAM::safeToSync(void)
2431 // delta interval went by
2432 clock_get_uptime(&delta
);
2434 // Figure it in seconds.
2435 absolutetime_to_nanoseconds(delta
, &delta_ns
);
2436 delta_secs
= (SInt32
)(delta_ns
/ NSEC_PER_SEC
);
2438 if ((delta_secs
> (_lastDeviceSync
+ MIN_SYNC_NOW_INTERVAL
)) || _freshInterval
) {
2439 _lastDeviceSync
= delta_secs
;
2440 _freshInterval
= FALSE
;