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 NVRAMLOCK() \
105 if (preemption_enabled() && !panic_active()) \
106 IOLockLock(_variableLock); \
109 #define NVRAMUNLOCK() \
111 if (preemption_enabled() && !panic_active()) \
112 IOLockUnlock(_variableLock); \
115 #define NVRAMLOCKASSERT() \
117 if (preemption_enabled() && !panic_active()) \
118 IOLockAssert(_variableLock, kIOLockAssertOwned); \
125 OSSharedPtr
<OSDictionary
> &dict
;
129 // Guid for Apple System Boot variables
130 // 40A0DDD2-77F8-4392-B4A3-1E7304206516
131 UUID_DEFINE(gAppleSystemVariableGuid
, 0x40, 0xA0, 0xDD, 0xD2, 0x77, 0xF8, 0x43, 0x92, 0xB4, 0xA3, 0x1E, 0x73, 0x04, 0x20, 0x65, 0x16);
133 // Apple NVRAM Variable namespace (APPLE_VENDOR_OS_VARIABLE_GUID)
134 // 7C436110-AB2A-4BBB-A880-FE41995C9F82
135 UUID_DEFINE(gAppleNVRAMGuid
, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82);
137 static bool gNVRAMLogging
= false;
139 // allowlist variables from macboot that need to be set/get from system region if present
140 static const char * const gNVRAMSystemList
[] = {
150 "auto-boot-halt-stage",
172 "com.apple.System.boot-nonce",
173 "com.apple.System.rtc-offset",
174 "com.apple.System.tz0-size",
185 "display-color-space",
188 "dpcd-max-brightness",
196 "enable-upgrade-fallback",
200 "failboot-breadcrumbs",
203 "force-upgrade-fail",
208 "iboot-failure-reason",
209 "iboot-failure-reason-str",
210 "iboot-failure-volume",
211 "iboot1-precommitted",
220 "mipi-bridge-cmd-verify",
221 "mipi-bridge-poll-cmd-fifo",
223 "one-time-boot-command",
228 "panicmedic-threshold",
229 "panicmedic-timestamps",
233 "policy-nonce-digests",
234 "preserve-debuggability",
235 "prevent-restores", // Keep for factory <rdar://problem/70476321>
237 "ramrod-kickstart-aces",
241 "reconfig-breakpoints",
242 "recovery-boot-mode",
243 "recovery-breadcrumbs",
244 "restored-host-timeout",
249 "StartupMuteAccessibility",
250 "storage-prev-assert",
251 "storage-prev-assert-stored",
254 "SystemAudioVolumeExtension",
255 "SystemAudioVolumeSaved",
257 "upgrade-fallback-boot-command",
267 IONVRAMVariableType type
;
271 VariableTypeEntry gVariableTypes
[] = {
272 {"auto-boot?", kOFVariableTypeBoolean
},
273 {"boot-args", kOFVariableTypeString
},
274 {"boot-command", kOFVariableTypeString
},
275 {"boot-device", kOFVariableTypeString
},
276 {"boot-file", kOFVariableTypeString
},
277 {"boot-screen", kOFVariableTypeString
},
278 {"boot-script", kOFVariableTypeString
},
279 {"console-screen", kOFVariableTypeString
},
280 {"default-client-ip", kOFVariableTypeString
},
281 {"default-gateway-ip", kOFVariableTypeString
},
282 {"default-mac-address?", kOFVariableTypeBoolean
},
283 {"default-router-ip", kOFVariableTypeString
},
284 {"default-server-ip", kOFVariableTypeString
},
285 {"default-subnet-mask", kOFVariableTypeString
},
286 {"diag-device", kOFVariableTypeString
},
287 {"diag-file", kOFVariableTypeString
},
288 {"diag-switch?", kOFVariableTypeBoolean
},
289 {"fcode-debug?", kOFVariableTypeBoolean
},
290 {"input-device", kOFVariableTypeString
},
291 {"input-device-1", kOFVariableTypeString
},
292 {"little-endian?", kOFVariableTypeBoolean
},
293 {"load-base", kOFVariableTypeNumber
},
294 {"mouse-device", kOFVariableTypeString
},
295 {"nvramrc", kOFVariableTypeString
},
296 {"oem-banner", kOFVariableTypeString
},
297 {"oem-banner?", kOFVariableTypeBoolean
},
298 {"oem-logo", kOFVariableTypeString
},
299 {"oem-logo?", kOFVariableTypeBoolean
},
300 {"output-device", kOFVariableTypeString
},
301 {"output-device-1", kOFVariableTypeString
},
302 {"pci-probe-list", kOFVariableTypeNumber
},
303 {"pci-probe-mask", kOFVariableTypeNumber
},
304 {"real-base", kOFVariableTypeNumber
},
305 {"real-mode?", kOFVariableTypeBoolean
},
306 {"real-size", kOFVariableTypeNumber
},
307 {"screen-#columns", kOFVariableTypeNumber
},
308 {"screen-#rows", kOFVariableTypeNumber
},
309 {"security-mode", kOFVariableTypeString
},
310 {"selftest-#megs", kOFVariableTypeNumber
},
311 {"use-generic?", kOFVariableTypeBoolean
},
312 {"use-nvramrc?", kOFVariableTypeBoolean
},
313 {"virt-base", kOFVariableTypeNumber
},
314 {"virt-size", kOFVariableTypeNumber
},
316 #if !defined(__x86_64__)
317 {"acc-cm-override-charger-count", kOFVariableTypeNumber
},
318 {"acc-cm-override-count", kOFVariableTypeNumber
},
319 {"acc-mb-ld-lifetime", kOFVariableTypeNumber
},
320 {"com.apple.System.boot-nonce", kOFVariableTypeString
},
321 {"darkboot", kOFVariableTypeBoolean
},
322 {"enter-tdm-mode", kOFVariableTypeBoolean
},
323 #endif /* !defined(__x86_64__) */
324 {nullptr, kOFVariableTypeData
} // Default type to return
327 union VariablePermission
{
329 uint64_t UserWrite
:1;
330 uint64_t RootRequired
:1;
331 uint64_t KernelOnly
:1;
332 uint64_t ResetNVRAMOnlyDelete
:1;
333 uint64_t NeverAllowedToDelete
:1;
334 uint64_t FullAccess
:1;
335 uint64_t Reserved
:58;
342 VariablePermission p
;
343 } VariablePermissionEntry
;
346 VariablePermissionEntry gVariablePermissions
[] = {
347 {"aapl,pci", .p
.Bits
.RootRequired
= 1},
348 {"battery-health", .p
.Bits
.RootRequired
= 1,
349 .p
.Bits
.NeverAllowedToDelete
= 1},
350 {"boot-image", .p
.Bits
.UserWrite
= 1},
351 {"com.apple.System.fp-state", .p
.Bits
.KernelOnly
= 1},
352 {"policy-nonce-digests", .p
.Bits
.ResetNVRAMOnlyDelete
= 1},
353 {"security-password", .p
.Bits
.RootRequired
= 1},
355 #if !defined(__x86_64__)
356 {"acc-cm-override-charger-count", .p
.Bits
.KernelOnly
= 1},
357 {"acc-cm-override-count", .p
.Bits
.KernelOnly
= 1},
358 {"acc-mb-ld-lifetime", .p
.Bits
.KernelOnly
= 1},
359 {"backlight-level", .p
.Bits
.UserWrite
= 1},
360 {"com.apple.System.boot-nonce", .p
.Bits
.KernelOnly
= 1},
361 {"com.apple.System.sep.art", .p
.Bits
.KernelOnly
= 1},
362 {"darkboot", .p
.Bits
.UserWrite
= 1},
363 {"nonce-seeds", .p
.Bits
.KernelOnly
= 1},
364 #endif /* !defined(__x86_64__) */
366 {nullptr, {.Bits
.FullAccess
= 1}} // Default access
369 static IONVRAMVariableType
370 getVariableType(const char *propName
)
372 const VariableTypeEntry
*entry
;
374 entry
= gVariableTypes
;
375 while (entry
->name
!= nullptr) {
376 if (strcmp(entry
->name
, propName
) == 0) {
385 static IONVRAMVariableType
386 getVariableType(const OSSymbol
*propSymbol
)
388 return getVariableType(propSymbol
->getCStringNoCopy());
391 static VariablePermission
392 getVariablePermission(const char *propName
)
394 const VariablePermissionEntry
*entry
;
396 entry
= gVariablePermissions
;
397 while (entry
->name
!= nullptr) {
398 if (strcmp(entry
->name
, propName
) == 0) {
408 variableInAllowList(const char *varName
)
412 while (gNVRAMSystemList
[i
] != nullptr) {
413 if (strcmp(varName
, gNVRAMSystemList
[i
]) == 0) {
423 verifyWriteSizeLimit(const uuid_t
*varGuid
, const char *variableName
, size_t propDataSize
)
425 if (variableInAllowList(variableName
)) {
426 if (strnstr(variableName
, "breadcrumbs", strlen(variableName
)) != NULL
) {
427 return propDataSize
<= 1024;
429 return propDataSize
<= 768;
437 verifyPermission(IONVRAMOperation op
, const uuid_t
*varGuid
, const char *varName
)
439 VariablePermission perm
;
440 bool kernel
, admin
, writeEntitled
, readEntitled
, allowList
, systemGuid
, systemEntitled
;
442 perm
= getVariablePermission(varName
);
444 kernel
= current_task() == kernel_task
;
446 if (perm
.Bits
.KernelOnly
) {
447 DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName
, kernel
);
451 allowList
= variableInAllowList(varName
);
452 systemGuid
= uuid_compare(*varGuid
, gAppleSystemVariableGuid
) == 0;
453 admin
= IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege
) == kIOReturnSuccess
;
454 writeEntitled
= IOTaskHasEntitlement(current_task(), kIONVRAMWriteAccessKey
);
455 readEntitled
= IOTaskHasEntitlement(current_task(), kIONVRAMReadAccessKey
);
456 systemEntitled
= IOTaskHasEntitlement(current_task(), kIONVRAMSystemAllowKey
) || kernel
;
459 case kIONVRAMOperationRead
:
460 if (kernel
|| admin
|| readEntitled
|| perm
.Bits
.FullAccess
) {
465 case kIONVRAMOperationWrite
:
466 if (kernel
|| perm
.Bits
.UserWrite
|| admin
|| writeEntitled
) {
469 if (!systemEntitled
) {
470 DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName
);
472 } else if (!systemEntitled
) {
473 DEBUG_ERROR("Not entitled for system region writes for %s\n", varName
);
481 case kIONVRAMOperationDelete
:
482 case kIONVRAMOperationObliterate
:
483 case kIONVRAMOperationReset
:
484 if (perm
.Bits
.NeverAllowedToDelete
) {
485 DEBUG_INFO("Never allowed to delete %s\n", varName
);
487 } else if ((op
== kIONVRAMOperationObliterate
) && perm
.Bits
.ResetNVRAMOnlyDelete
) {
488 DEBUG_INFO("Not allowed to obliterate %s\n", varName
);
492 if (kernel
|| perm
.Bits
.UserWrite
|| admin
|| writeEntitled
) {
495 if (!systemEntitled
) {
496 DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName
);
498 } else if (!systemEntitled
) {
499 DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName
);
508 DEBUG_INFO("Permission for %s denied, kernel=%d, admin=%d, writeEntitled=%d, readEntitled=%d, systemGuid=%d, systemEntitled=%d\n",
509 varName
, kernel
, admin
, writeEntitled
, readEntitled
, systemGuid
, systemEntitled
);
514 verifyPermission(IONVRAMOperation op
, const uuid_t
*varGuid
, const OSSymbol
*varName
)
516 return verifyPermission(op
, varGuid
, varName
->getCStringNoCopy());
520 * Parse a variable name of the form "GUID:name".
521 * If the name cannot be parsed, substitute the Apple global variable GUID.
522 * Returns TRUE if a GUID was found in the name, FALSE otherwise.
523 * The guidResult and nameResult arguments may be nullptr if you just want
524 * to check the format of the string.
527 parseVariableName(const char *key
, uuid_t
*guidResult
, const char **nameResult
)
529 uuid_string_t temp
= {0};
530 size_t keyLen
= strlen(key
);
532 const char *name
= key
;
535 if (keyLen
> sizeof(temp
)) {
536 // check for at least UUID + ":" + more
537 memcpy(temp
, key
, sizeof(temp
) - 1);
539 if ((uuid_parse(temp
, guid
) == 0) &&
540 (key
[sizeof(temp
) - 1] == ':')) {
541 name
= key
+ sizeof(temp
);
547 result
? uuid_copy(*guidResult
, guid
) : uuid_copy(*guidResult
, gAppleNVRAMGuid
);
556 // private IOService based class for publishing distinct dictionary properties on
557 // for easy ioreg access since the serializeProperties call is overloaded and is used
558 // as variable access
559 class IODTNVRAMVariables
: public IOService
561 OSDeclareDefaultStructors(IODTNVRAMVariables
)
563 IODTNVRAM
*_provider
;
564 OSDictionary
*_properties
;
568 bool init(const uuid_t
*guid
);
569 virtual bool start(IOService
* provider
) APPLE_KEXT_OVERRIDE
;
570 virtual IOReturn
setProperties(OSObject
* properties
) APPLE_KEXT_OVERRIDE
;
571 virtual bool serializeProperties(OSSerialize
*s
) const APPLE_KEXT_OVERRIDE
;
574 OSDefineMetaClassAndStructors(IODTNVRAMVariables
, IOService
)
577 IODTNVRAMVariables::init(const uuid_t
*guid
)
579 require(super::init(), error
);
580 require(guid
, error
);
582 uuid_copy(_guid
, *guid
);
591 IODTNVRAMVariables::start(IOService
* provider
)
593 require(IOService::start(provider
), error
);
595 require(_provider
= OSDynamicCast(IODTNVRAM
, provider
), error
);
608 IODTNVRAMVariables::setProperties(OSObject
* properties
)
610 if (OSDynamicCast(OSDictionary
, properties
)) {
611 OSSafeReleaseNULL(_properties
);
612 _properties
= OSDynamicCast(OSDictionary
, properties
);
613 properties
->retain();
616 return IOService::setProperties(properties
);
620 IODTNVRAMVariables::serializeProperties(OSSerialize
*s
) const
623 OSSharedPtr
<OSDictionary
> dict
;
624 OSSharedPtr
<OSCollectionIterator
> iter
;
625 OSSharedPtr
<OSDictionary
> localProperties(_properties
, OSRetain
);
628 require(localProperties
!= nullptr, exit
);
630 dict
= OSDictionary::withCapacity(localProperties
->getCount());
631 require_action(dict
, exit
, DEBUG_ERROR("No dictionary\n"));
633 iter
= OSCollectionIterator::withCollection(localProperties
.get());
634 require_action(iter
, exit
, DEBUG_ERROR("failed to create iterator\n"));
636 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
637 if (verifyPermission(kIONVRAMOperationRead
, &_guid
, key
)) {
638 dict
->setObject(key
, localProperties
->getObject(key
));
642 result
= dict
->serialize(s
);
645 DEBUG_INFO("result=%d\n", result
);
650 IODTNVRAM::init(IORegistryEntry
*old
, const IORegistryPlane
*plane
)
652 OSSharedPtr
<OSDictionary
> dict
;
654 if (!super::init(old
, plane
)) {
658 _variableLock
= IOLockAlloc();
659 if (!_variableLock
) {
663 PE_parse_boot_argn("nvram-log", &gNVRAMLogging
, sizeof(gNVRAMLogging
));
665 dict
= OSDictionary::withCapacity(1);
666 if (dict
== nullptr) {
669 setPropertyTable(dict
.get());
672 _nvramSize
= getNVRAMSize();
673 if (_nvramSize
== 0) {
674 DEBUG_ERROR("NVRAM : Error - default size not specified in DT\n");
677 // partition offsets are UInt16 (bytes / 0x10) + 1
678 if (_nvramSize
> 0xFFFF * 0x10) {
679 DEBUG_ERROR("NVRAM : truncating _nvramSize from %ld\n", (long) _nvramSize
);
680 _nvramSize
= 0xFFFF * 0x10;
682 _nvramImage
= IONew(UInt8
, _nvramSize
);
683 if (_nvramImage
== nullptr) {
687 _nvramPartitionOffsets
= OSDictionary::withCapacity(1);
688 if (_nvramPartitionOffsets
== nullptr) {
692 _nvramPartitionLengths
= OSDictionary::withCapacity(1);
693 if (_nvramPartitionLengths
== nullptr) {
697 _registryPropertiesKey
= OSSymbol::withCStringNoCopy("aapl,pci");
698 if (_registryPropertiesKey
== nullptr) {
702 // <rdar://problem/9529235> race condition possible between
703 // IODTNVRAM and IONVRAMController (restore loses boot-args)
706 // Require at least the common partition to be present and error free
707 if (_commonDict
== nullptr) {
715 IODTNVRAM::initProxyData(void)
717 OSSharedPtr
<IORegistryEntry
> entry
;
718 const char *key
= "nvram-proxy-data";
722 entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
);
723 if (entry
!= nullptr) {
724 OSSharedPtr
<OSObject
> prop
= entry
->copyProperty(key
);
725 if (prop
!= nullptr) {
726 data
= OSDynamicCast(OSData
, prop
.get());
727 if (data
!= nullptr) {
728 bytes
= data
->getBytesNoCopy();
729 if ((bytes
!= nullptr) && (data
->getLength() <= _nvramSize
)) {
730 bcopy(bytes
, _nvramImage
, data
->getLength());
736 entry
->removeProperty(key
);
741 IODTNVRAM::getNVRAMSize(void)
743 OSSharedPtr
<IORegistryEntry
> entry
;
744 const char *key
= "nvram-total-size";
748 entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
);
749 if (entry
!= nullptr) {
750 OSSharedPtr
<OSObject
> prop
= entry
->copyProperty(key
);
751 if (prop
!= nullptr) {
752 data
= OSDynamicCast(OSData
, prop
.get());
753 if (data
!= nullptr) {
754 size
= *((UInt32
*)data
->getBytesNoCopy());
755 DEBUG_ALWAYS("NVRAM size is %u bytes\n", (unsigned int) size
);
764 IODTNVRAM::registerNVRAMController(IONVRAMController
*nvram
)
766 if (_nvramController
!= nullptr) {
767 DEBUG_ERROR("Duplicate controller set\n");
771 DEBUG_INFO("setting controller\n");
773 _nvramController
= nvram
;
775 // <rdar://problem/9529235> race condition possible between
776 // IODTNVRAM and IONVRAMController (restore loses boot-args)
778 DEBUG_INFO("Proxied NVRAM data\n");
779 _nvramController
->read(0, _nvramImage
, _nvramSize
);
783 if (_systemPartitionSize
) {
784 _systemService
= new IODTNVRAMVariables
;
786 if (!_systemService
|| !_systemService
->init(&gAppleSystemVariableGuid
)) {
787 DEBUG_ERROR("Unable to start the system service!\n");
791 _systemService
->setName("options-system");
793 if (!_systemService
->attach(this)) {
794 DEBUG_ERROR("Unable to attach the system service!\n");
795 OSSafeReleaseNULL(_systemService
);
799 if (!_systemService
->start(this)) {
800 DEBUG_ERROR("Unable to start the system service!\n");
801 _systemService
->detach(this);
802 OSSafeReleaseNULL(_systemService
);
808 if (_commonPartitionSize
) {
809 _commonService
= new IODTNVRAMVariables
;
811 if (!_commonService
|| !_commonService
->init(&gAppleNVRAMGuid
)) {
812 DEBUG_ERROR("Unable to start the common service!\n");
816 _commonService
->setName("options-common");
818 if (!_commonService
->attach(this)) {
819 DEBUG_ERROR("Unable to attach the common service!\n");
820 OSSafeReleaseNULL(_commonService
);
824 if (!_commonService
->start(this)) {
825 DEBUG_ERROR("Unable to start the common service!\n");
826 _systemService
->detach(this);
827 OSSafeReleaseNULL(_commonService
);
834 (void) syncVariables();
839 IODTNVRAM::initNVRAMImage(void)
841 char partitionID
[18];
842 UInt32 partitionOffset
, partitionLength
;
843 UInt32 currentLength
, currentOffset
= 0;
845 _commonPartitionOffset
= 0xFFFFFFFF;
846 _systemPartitionOffset
= 0xFFFFFFFF;
848 // Look through the partitions to find the OF and System partitions.
849 while (currentOffset
< _nvramSize
) {
850 bool common_partition
;
851 bool system_partition
;
853 chrp_nvram_header_t
* header
= (chrp_nvram_header_t
*)(_nvramImage
+ currentOffset
);
855 currentLength
= header
->len
* NVRAM_CHRP_LENGTH_BLOCK_SIZE
;
857 if (currentLength
< sizeof(chrp_nvram_header_t
)) {
861 partitionOffset
= currentOffset
+ sizeof(chrp_nvram_header_t
);
862 partitionLength
= currentLength
- sizeof(chrp_nvram_header_t
);
864 if ((partitionOffset
+ partitionLength
) > _nvramSize
) {
868 common_partition
= memcmp(header
->name
, NVRAM_CHRP_PARTITION_NAME_COMMON
, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON
)) == 0;
869 system_partition
= (memcmp(header
->name
, NVRAM_CHRP_PARTITION_NAME_SYSTEM
, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM
)) == 0) ||
870 (memcmp(header
->name
, NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY
, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY
)) == 0);
872 if (common_partition
) {
873 _commonPartitionOffset
= partitionOffset
;
874 _commonPartitionSize
= partitionLength
;
875 } else if (system_partition
) {
876 _systemPartitionOffset
= partitionOffset
;
877 _systemPartitionSize
= partitionLength
;
879 OSSharedPtr
<OSNumber
> partitionOffsetNumber
, partitionLengthNumber
;
881 // Construct the partition ID from the signature and name.
882 snprintf(partitionID
, sizeof(partitionID
), "0x%02x,", header
->sig
);
883 strncpy(partitionID
+ 5, header
->name
, sizeof(header
->name
));
884 partitionID
[17] = '\0';
886 partitionOffsetNumber
= OSNumber::withNumber(partitionOffset
, 32);
887 partitionLengthNumber
= OSNumber::withNumber(partitionLength
, 32);
889 // Save the partition offset and length
890 _nvramPartitionOffsets
->setObject(partitionID
, partitionOffsetNumber
.get());
891 _nvramPartitionLengths
->setObject(partitionID
, partitionLengthNumber
.get());
893 currentOffset
+= currentLength
;
896 if (_commonPartitionOffset
!= 0xFFFFFFFF) {
897 _commonImage
= _nvramImage
+ _commonPartitionOffset
;
900 if (_systemPartitionOffset
!= 0xFFFFFFFF) {
901 _systemImage
= _nvramImage
+ _systemPartitionOffset
;
904 DEBUG_ALWAYS("NVRAM : ofPartitionOffset - 0x%x, ofPartitionSize - 0x%x, systemPartitionOffset - 0x%x, systemPartitionSize - 0x%x\n",
905 (unsigned int) _commonPartitionOffset
, (unsigned int) _commonPartitionSize
, (unsigned int) _systemPartitionOffset
, (unsigned int) _systemPartitionSize
);
908 _freshInterval
= TRUE
; // we will allow sync() even before the first 15 minutes have passed.
914 IODTNVRAM::syncInternal(bool rateLimit
)
916 DEBUG_INFO("rateLimit=%d\n", rateLimit
);
918 // Don't try to perform controller operations if none has been registered.
919 if (_nvramController
== nullptr) {
923 // Rate limit requests to sync. Drivers that need this rate limiting will
924 // shadow the data and only write to flash when they get a sync call
925 if (rateLimit
&& !safeToSync()) {
929 DEBUG_INFO("Calling sync()\n");
931 _nvramController
->sync();
936 IODTNVRAM::sync(void)
942 IODTNVRAM::serializeProperties(OSSerialize
*s
) const
945 OSSharedPtr
<OSDictionary
> dict
;
946 OSSharedPtr
<OSCollectionIterator
> iter
;
948 unsigned int totalCapacity
= 0;
952 totalCapacity
+= _commonDict
->getCapacity();
956 totalCapacity
+= _systemDict
->getCapacity();
959 dict
= OSDictionary::withCapacity(totalCapacity
);
961 if (dict
== nullptr) {
962 DEBUG_ERROR("No dictionary\n");
966 // Copy system entries first if present then copy unique common entries
967 if (_systemDict
!= nullptr) {
968 iter
= OSCollectionIterator::withCollection(_systemDict
.get());
969 if (iter
== nullptr) {
970 DEBUG_ERROR("failed to create iterator\n");
974 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
975 if (verifyPermission(kIONVRAMOperationRead
, &gAppleSystemVariableGuid
, key
)) {
976 dict
->setObject(key
, _systemDict
->getObject(key
));
983 if (_commonDict
!= nullptr) {
984 iter
= OSCollectionIterator::withCollection(_commonDict
.get());
985 if (iter
== nullptr) {
986 DEBUG_ERROR("failed to create common iterator\n");
990 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
991 if (dict
->getObject(key
) != nullptr) {
995 if (verifyPermission(kIONVRAMOperationRead
, &gAppleNVRAMGuid
, key
)) {
996 dict
->setObject(key
, _commonDict
->getObject(key
));
1001 result
= dict
->serialize(s
);
1006 DEBUG_INFO("result=%d\n", result
);
1012 IODTNVRAM::chooseDictionary(IONVRAMOperation operation
, const uuid_t
*varGuid
, const char *variableName
, OSDictionary
**dict
) const
1014 if (_systemDict
!= nullptr) {
1015 bool systemGuid
= uuid_compare(*varGuid
, gAppleSystemVariableGuid
) == 0;
1017 if (variableInAllowList(variableName
)) {
1018 DEBUG_INFO("Using system dictionary due to allow list\n");
1020 DEBUG_ERROR("System GUID NOT used for %s\n", variableName
);
1022 *dict
= _systemDict
.get();
1023 } else if (systemGuid
) {
1024 DEBUG_INFO("Using system dictionary via GUID\n");
1025 *dict
= _systemDict
.get();
1027 DEBUG_INFO("Using common dictionary\n");
1028 *dict
= _commonDict
.get();
1031 DEBUG_INFO("Defaulting to common dictionary\n");
1032 *dict
= _commonDict
.get();
1035 return kIOReturnSuccess
;
1039 IODTNVRAM::handleSpecialVariables(const char *name
, uuid_t
*guid
, OSObject
*obj
, IOReturn
*error
)
1041 IOReturn err
= kIOReturnSuccess
;
1042 bool special
= false;
1046 if (strcmp(name
, "ResetNVRam") == 0) {
1047 DEBUG_INFO("%s requested\n", name
);
1049 if (uuid_compare(*guid
, gAppleSystemVariableGuid
) == 0) {
1050 if (_systemDict
!= nullptr) {
1051 _systemDict
->flushCollection();
1054 _commonDict
->flushCollection();
1055 DEBUG_INFO("system & common dictionary flushed\n");
1057 err
= syncVariables();
1061 } else if (strcmp(name
, "ObliterateNVRam") == 0) {
1062 DEBUG_INFO("%s requested\n", name
);
1064 if ((_systemDict
!= nullptr) && (uuid_compare(*guid
, gAppleSystemVariableGuid
) == 0)) {
1065 const OSSymbol
*key
;
1066 OSSharedPtr
<OSDictionary
> newDict
;
1067 OSSharedPtr
<OSCollectionIterator
> iter
;
1069 newDict
= OSDictionary::withCapacity(_systemDict
->getCapacity());
1070 iter
= OSCollectionIterator::withCollection(newDict
.get());
1071 if ((newDict
== nullptr) || (iter
== nullptr)) {
1072 err
= kIOReturnNoMemory
;
1076 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
1077 const OSSymbol
*key
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
1078 if (key
== nullptr) {
1079 err
= kIOReturnNoMemory
;
1083 if (!verifyPermission(kIONVRAMOperationObliterate
, &gAppleSystemVariableGuid
, key
)) {
1084 newDict
->setObject(key
, _systemDict
->getObject(key
));
1088 _systemDict
= newDict
;
1090 DEBUG_INFO("system dictionary flushed\n");
1091 } else if (_commonDict
!= nullptr) {
1092 const OSSymbol
*key
;
1093 OSSharedPtr
<OSDictionary
> newDict
;
1094 OSSharedPtr
<OSCollectionIterator
> iter
;
1096 newDict
= OSDictionary::withCapacity(_commonDict
->getCapacity());
1097 iter
= OSCollectionIterator::withCollection(newDict
.get());
1098 if ((newDict
== nullptr) || (iter
== nullptr)) {
1099 err
= kIOReturnNoMemory
;
1103 while ((key
= OSDynamicCast(OSSymbol
, iter
->getNextObject()))) {
1104 if (!verifyPermission(kIONVRAMOperationObliterate
, &gAppleNVRAMGuid
, key
)) {
1105 newDict
->setObject(key
, _commonDict
->getObject(key
));
1109 _commonDict
= newDict
;
1111 DEBUG_INFO("common dictionary flushed\n");
1115 err
= syncVariables();
1126 OSSharedPtr
<OSObject
>
1127 IODTNVRAM::copyProperty(const OSSymbol
*aKey
) const
1130 const char *variableName
;
1133 OSSharedPtr
<OSObject
> theObject
= nullptr;
1135 DEBUG_INFO("aKey=%s\n", aKey
->getCStringNoCopy());
1137 parseVariableName(aKey
->getCStringNoCopy(), &varGuid
, &variableName
);
1139 result
= chooseDictionary(kIONVRAMOperationRead
, &varGuid
, variableName
, &dict
);
1140 if (result
!= kIOReturnSuccess
) {
1144 if (!verifyPermission(kIONVRAMOperationRead
, &varGuid
, variableName
)) {
1145 DEBUG_INFO("Not privileged\n");
1150 theObject
.reset(dict
->getObject(variableName
), OSRetain
);
1153 if (theObject
!= nullptr) {
1154 DEBUG_INFO("found data\n");
1161 OSSharedPtr
<OSObject
>
1162 IODTNVRAM::copyProperty(const char *aKey
) const
1164 OSSharedPtr
<const OSSymbol
> keySymbol
;
1165 OSSharedPtr
<OSObject
> theObject
;
1167 keySymbol
= OSSymbol::withCString(aKey
);
1168 if (keySymbol
!= nullptr) {
1169 theObject
= copyProperty(keySymbol
.get());
1176 IODTNVRAM::getProperty(const OSSymbol
*aKey
) const
1178 // The shared pointer gets released at the end of the function,
1179 // and returns a view into theObject.
1180 OSSharedPtr
<OSObject
> theObject
= copyProperty(aKey
);
1182 return theObject
.get();
1186 IODTNVRAM::getProperty(const char *aKey
) const
1188 // The shared pointer gets released at the end of the function,
1189 // and returns a view into theObject.
1190 OSSharedPtr
<OSObject
> theObject
= copyProperty(aKey
);
1192 return theObject
.get();
1196 IODTNVRAM::setPropertyInternal(const OSSymbol
*aKey
, OSObject
*anObject
)
1198 IOReturn result
= kIOReturnSuccess
;
1199 bool remove
= false;
1200 OSString
*tmpString
= nullptr;
1201 OSSharedPtr
<OSObject
> propObject
, oldObject
;
1202 OSSharedPtr
<OSObject
> sharedObject(anObject
, OSRetain
);
1203 const char *variableName
;
1206 bool deletePropertyKey
, syncNowPropertyKey
, forceSyncNowPropertyKey
;
1207 size_t propDataSize
= 0;
1209 DEBUG_INFO("aKey=%s\n", aKey
->getCStringNoCopy());
1211 parseVariableName(aKey
->getCStringNoCopy(), &varGuid
, &variableName
);
1212 deletePropertyKey
= strncmp(variableName
, kIONVRAMDeletePropertyKey
, sizeof(kIONVRAMDeletePropertyKey
)) == 0;
1213 syncNowPropertyKey
= strncmp(variableName
, kIONVRAMSyncNowPropertyKey
, sizeof(kIONVRAMSyncNowPropertyKey
)) == 0;
1214 forceSyncNowPropertyKey
= strncmp(variableName
, kIONVRAMForceSyncNowPropertyKey
, sizeof(kIONVRAMForceSyncNowPropertyKey
)) == 0;
1216 if (deletePropertyKey
) {
1217 tmpString
= OSDynamicCast(OSString
, anObject
);
1218 if (tmpString
!= nullptr) {
1219 DEBUG_INFO("kIONVRAMDeletePropertyKey found\n");
1220 OSSharedPtr
<const OSSymbol
> sharedKey
= OSSymbol::withString(tmpString
);
1221 removeProperty(sharedKey
.get());
1223 DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
1224 result
= kIOReturnError
;
1227 } else if (syncNowPropertyKey
|| forceSyncNowPropertyKey
) {
1228 tmpString
= OSDynamicCast(OSString
, anObject
);
1229 DEBUG_INFO("NVRAM sync key %s found\n", aKey
->getCStringNoCopy());
1230 if (tmpString
!= nullptr) {
1231 // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
1232 syncInternal(syncNowPropertyKey
);
1234 DEBUG_INFO("%s value needs to be an OSString\n", variableName
);
1235 result
= kIOReturnError
;
1240 result
= chooseDictionary(kIONVRAMOperationWrite
, &varGuid
, variableName
, &dict
);
1241 if (result
!= kIOReturnSuccess
) {
1245 if (!verifyPermission(kIONVRAMOperationWrite
, &varGuid
, variableName
)) {
1246 DEBUG_INFO("Not privileged\n");
1247 result
= kIOReturnNotPrivileged
;
1251 // Make sure the object is of the correct type.
1252 switch (getVariableType(variableName
)) {
1253 case kOFVariableTypeBoolean
:
1254 propObject
= OSDynamicPtrCast
<OSBoolean
>(sharedObject
);
1257 case kOFVariableTypeNumber
:
1258 propObject
= OSDynamicPtrCast
<OSNumber
>(sharedObject
);
1261 case kOFVariableTypeString
:
1262 propObject
= OSDynamicPtrCast
<OSString
>(sharedObject
);
1263 if (propObject
!= nullptr) {
1264 propDataSize
= (OSDynamicPtrCast
<OSString
>(propObject
))->getLength();
1266 if (aKey
->isEqualTo(kIONVRAMBootArgsKey
) && (propDataSize
>= BOOT_LINE_LENGTH
)) {
1267 DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize
);
1268 result
= kIOReturnNoSpace
;
1274 case kOFVariableTypeData
:
1275 propObject
= OSDynamicPtrCast
<OSData
>(sharedObject
);
1276 if (propObject
== nullptr) {
1277 tmpString
= OSDynamicCast(OSString
, sharedObject
.get());
1278 if (tmpString
!= nullptr) {
1279 propObject
= OSData::withBytes(tmpString
->getCStringNoCopy(),
1280 tmpString
->getLength());
1284 if (propObject
!= nullptr) {
1285 propDataSize
= (OSDynamicPtrCast
<OSData
>(propObject
))->getLength();
1288 #if defined(XNU_TARGET_OS_OSX)
1289 if ((propObject
!= nullptr) && ((OSDynamicPtrCast
<OSData
>(propObject
))->getLength() == 0)) {
1292 #endif /* defined(XNU_TARGET_OS_OSX) */
1298 if (propObject
== nullptr) {
1299 DEBUG_INFO("No property object\n");
1300 result
= kIOReturnBadArgument
;
1304 if (!verifyWriteSizeLimit(&varGuid
, variableName
, propDataSize
)) {
1305 DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize
, variableName
);
1306 result
= kIOReturnNoSpace
;
1312 if (handleSpecialVariables(variableName
, &varGuid
, propObject
.get(), &result
)) {
1316 oldObject
.reset(dict
->getObject(variableName
), OSRetain
);
1317 if (remove
== false) {
1318 DEBUG_INFO("Adding object\n");
1319 if (!dict
->setObject(variableName
, propObject
.get())) {
1320 result
= kIOReturnBadArgument
;
1323 DEBUG_INFO("Removing object\n");
1324 // Check for existence so we can decide whether we need to sync variables
1326 result
= removePropertyInternal(aKey
);
1328 result
= kIOReturnNotFound
;
1332 if (result
== kIOReturnSuccess
) {
1333 result
= syncVariables();
1334 if (result
!= kIOReturnSuccess
) {
1335 DEBUG_ERROR("syncVariables failed, result=0x%08x\n", result
);
1337 dict
->setObject(variableName
, oldObject
.get());
1339 dict
->removeObject(variableName
);
1341 (void) syncVariables();
1342 result
= kIOReturnNoMemory
;
1357 DEBUG_INFO("result=0x%08x\n", result
);
1363 IODTNVRAM::setProperty(const OSSymbol
*aKey
, OSObject
*anObject
)
1365 return setPropertyInternal(aKey
, anObject
) == kIOReturnSuccess
;
1369 IODTNVRAM::removeProperty(const OSSymbol
*aKey
)
1375 ret
= removePropertyInternal(aKey
);
1379 if (ret
!= kIOReturnSuccess
) {
1380 DEBUG_INFO("removePropertyInternal failed, ret=0x%08x\n", ret
);
1385 IODTNVRAM::removePropertyInternal(const OSSymbol
*aKey
)
1388 const char *variableName
;
1392 DEBUG_INFO("aKey=%s\n", aKey
->getCStringNoCopy());
1396 parseVariableName(aKey
->getCStringNoCopy(), &varGuid
, &variableName
);
1398 result
= chooseDictionary(kIONVRAMOperationDelete
, &varGuid
, variableName
, &dict
);
1399 if (result
!= kIOReturnSuccess
) {
1403 if (!verifyPermission(kIONVRAMOperationDelete
, &varGuid
, variableName
)) {
1404 DEBUG_INFO("Not priveleged\n");
1405 result
= kIOReturnNotPrivileged
;
1409 // If the object exists, remove it from the dictionary.
1410 if (dict
->getObject(variableName
) != nullptr) {
1411 dict
->removeObject(variableName
);
1412 result
= syncVariables();
1420 IODTNVRAM::setProperties(OSObject
*properties
)
1422 IOReturn result
= kIOReturnSuccess
;
1424 const OSSymbol
*key
;
1426 OSSharedPtr
<OSCollectionIterator
> iter
;
1428 dict
= OSDynamicCast(OSDictionary
, properties
);
1429 if (dict
== nullptr) {
1430 DEBUG_ERROR("Not a dictionary\n");
1431 return kIOReturnBadArgument
;
1434 iter
= OSCollectionIterator::withCollection(dict
);
1435 if (iter
== nullptr) {
1436 DEBUG_ERROR("Couldn't create iterator\n");
1437 return kIOReturnBadArgument
;
1440 while (result
== kIOReturnSuccess
) {
1441 key
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
1442 if (key
== nullptr) {
1446 object
= dict
->getObject(key
);
1447 if (object
== nullptr) {
1451 result
= setPropertyInternal(key
, object
);
1454 DEBUG_INFO("result=0x%08x\n", result
);
1460 IODTNVRAM::readXPRAM(IOByteCount offset
, UInt8
*buffer
,
1463 return kIOReturnUnsupported
;
1467 IODTNVRAM::writeXPRAM(IOByteCount offset
, UInt8
*buffer
,
1470 return kIOReturnUnsupported
;
1474 IODTNVRAM::readNVRAMProperty(IORegistryEntry
*entry
,
1475 const OSSymbol
**name
,
1480 err
= readNVRAMPropertyType1(entry
, name
, value
);
1486 IODTNVRAM::writeNVRAMProperty(IORegistryEntry
*entry
,
1487 const OSSymbol
*name
,
1492 err
= writeNVRAMPropertyType1(entry
, name
, value
);
1498 IODTNVRAM::getNVRAMPartitions(void)
1500 return _nvramPartitionLengths
.get();
1504 IODTNVRAM::readNVRAMPartition(const OSSymbol
*partitionID
,
1505 IOByteCount offset
, UInt8
*buffer
,
1508 OSNumber
*partitionOffsetNumber
, *partitionLengthNumber
;
1509 UInt32 partitionOffset
, partitionLength
, end
;
1511 partitionOffsetNumber
=
1512 (OSNumber
*)_nvramPartitionOffsets
->getObject(partitionID
);
1513 partitionLengthNumber
=
1514 (OSNumber
*)_nvramPartitionLengths
->getObject(partitionID
);
1516 if ((partitionOffsetNumber
== nullptr) || (partitionLengthNumber
== nullptr)) {
1517 return kIOReturnNotFound
;
1520 partitionOffset
= partitionOffsetNumber
->unsigned32BitValue();
1521 partitionLength
= partitionLengthNumber
->unsigned32BitValue();
1523 if (os_add_overflow(offset
, length
, &end
)) {
1524 return kIOReturnBadArgument
;
1526 if ((buffer
== nullptr) || (length
== 0) || (end
> partitionLength
)) {
1527 return kIOReturnBadArgument
;
1530 bcopy(_nvramImage
+ partitionOffset
+ offset
, buffer
, length
);
1532 return kIOReturnSuccess
;
1536 IODTNVRAM::writeNVRAMPartition(const OSSymbol
*partitionID
,
1537 IOByteCount offset
, UInt8
*buffer
,
1540 OSNumber
*partitionOffsetNumber
, *partitionLengthNumber
;
1541 UInt32 partitionOffset
, partitionLength
, end
;
1543 partitionOffsetNumber
=
1544 (OSNumber
*)_nvramPartitionOffsets
->getObject(partitionID
);
1545 partitionLengthNumber
=
1546 (OSNumber
*)_nvramPartitionLengths
->getObject(partitionID
);
1548 if ((partitionOffsetNumber
== nullptr) || (partitionLengthNumber
== nullptr)) {
1549 return kIOReturnNotFound
;
1552 partitionOffset
= partitionOffsetNumber
->unsigned32BitValue();
1553 partitionLength
= partitionLengthNumber
->unsigned32BitValue();
1555 if (os_add_overflow(offset
, length
, &end
)) {
1556 return kIOReturnBadArgument
;
1558 if ((buffer
== nullptr) || (length
== 0) || (end
> partitionLength
)) {
1559 return kIOReturnBadArgument
;
1562 bcopy(buffer
, _nvramImage
+ partitionOffset
+ offset
, length
);
1564 if (_nvramController
!= nullptr) {
1565 _nvramController
->write(0, _nvramImage
, _nvramSize
);
1568 return kIOReturnSuccess
;
1572 IODTNVRAM::savePanicInfo(UInt8
*buffer
, IOByteCount length
)
1580 IODTNVRAM::calculatePartitionChecksum(UInt8
*partitionHeader
)
1582 UInt8 cnt
, isum
, csum
= 0;
1584 for (cnt
= 0; cnt
< 0x10; cnt
++) {
1585 isum
= csum
+ partitionHeader
[cnt
];
1596 IODTNVRAM::initVariables(void)
1599 UInt8
*propName
, *propData
;
1600 UInt32 propNameLength
, propDataLength
, regionIndex
;
1601 OSSharedPtr
<const OSSymbol
> propSymbol
;
1602 OSSharedPtr
<OSObject
> propObject
;
1603 NVRAMRegionInfo
*currentRegion
;
1605 NVRAMRegionInfo variableRegions
[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON
, _commonPartitionOffset
, _commonPartitionSize
, _commonDict
, _commonImage
},
1606 { NVRAM_CHRP_PARTITION_NAME_SYSTEM
, _systemPartitionOffset
, _systemPartitionSize
, _systemDict
, _systemImage
} };
1608 DEBUG_INFO("...\n");
1610 for (regionIndex
= 0; regionIndex
< ARRAY_SIZE(variableRegions
); regionIndex
++) {
1611 currentRegion
= &variableRegions
[regionIndex
];
1613 if (currentRegion
->size
== 0) {
1617 currentRegion
->dict
= OSDictionary::withCapacity(1);
1619 DEBUG_INFO("region = %s\n", currentRegion
->name
);
1621 while (cnt
< currentRegion
->size
) {
1622 // Break if there is no name.
1623 if (currentRegion
->image
[cnt
] == '\0') {
1627 // Find the length of the name.
1628 propName
= currentRegion
->image
+ cnt
;
1629 for (propNameLength
= 0; (cnt
+ propNameLength
) < currentRegion
->size
;
1631 if (currentRegion
->image
[cnt
+ propNameLength
] == '=') {
1636 // Break if the name goes past the end of the partition.
1637 if ((cnt
+ propNameLength
) >= currentRegion
->size
) {
1640 cnt
+= propNameLength
+ 1;
1642 propData
= currentRegion
->image
+ cnt
;
1643 for (propDataLength
= 0; (cnt
+ propDataLength
) < currentRegion
->size
;
1645 if (currentRegion
->image
[cnt
+ propDataLength
] == '\0') {
1650 // Break if the data goes past the end of the partition.
1651 if ((cnt
+ propDataLength
) >= currentRegion
->size
) {
1654 cnt
+= propDataLength
+ 1;
1656 if (convertPropToObject(propName
, propNameLength
,
1657 propData
, propDataLength
,
1658 propSymbol
, propObject
)) {
1659 DEBUG_INFO("adding %s, dataLength=%u\n", propSymbol
.get()->getCStringNoCopy(), (unsigned int)propDataLength
);
1660 currentRegion
->dict
.get()->setObject(propSymbol
.get(), propObject
.get());
1665 // Create the boot-args property if it is not in the dictionary.
1666 if (_commonDict
->getObject(kIONVRAMBootArgsKey
) == nullptr) {
1667 propObject
= OSString::withCStringNoCopy("");
1668 if (propObject
!= nullptr) {
1669 _commonDict
->setObject(kIONVRAMBootArgsKey
, propObject
.get());
1673 DEBUG_INFO("%s _commonDict=%p _systemDict=%p\n", __FUNCTION__
, _commonDict
.get(), _systemDict
.get());
1675 return kIOReturnSuccess
;
1679 IODTNVRAM::syncOFVariables(void)
1681 return kIOReturnUnsupported
;
1685 IODTNVRAM::syncVariables(void)
1688 UInt32 length
, maxLength
, regionIndex
;
1689 UInt8
*buffer
, *tmpBuffer
;
1690 const OSSymbol
*tmpSymbol
;
1691 OSObject
*tmpObject
;
1692 OSSharedPtr
<OSCollectionIterator
> iter
;
1693 NVRAMRegionInfo
*currentRegion
;
1695 NVRAMRegionInfo variableRegions
[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON
, _commonPartitionOffset
, _commonPartitionSize
, _commonDict
, _commonImage
},
1696 { NVRAM_CHRP_PARTITION_NAME_SYSTEM
, _systemPartitionOffset
, _systemPartitionSize
, _systemDict
, _systemImage
} };
1700 if (_systemPanicked
) {
1701 return kIOReturnNotReady
;
1704 if (_nvramController
== nullptr) {
1705 DEBUG_ERROR("No _nvramController\n");
1706 return kIOReturnNotReady
;
1709 DEBUG_INFO("...\n");
1711 for (regionIndex
= 0; regionIndex
< ARRAY_SIZE(variableRegions
); regionIndex
++) {
1712 OSSharedPtr
<OSNumber
> sizeUsed
;
1713 currentRegion
= &variableRegions
[regionIndex
];
1715 if (currentRegion
->size
== 0) {
1719 DEBUG_INFO("region = %s\n", currentRegion
->name
);
1720 buffer
= tmpBuffer
= IONew(UInt8
, currentRegion
->size
);
1721 if (buffer
== nullptr) {
1722 return kIOReturnNoMemory
;
1724 bzero(buffer
, currentRegion
->size
);
1727 maxLength
= currentRegion
->size
;
1729 iter
= OSCollectionIterator::withCollection(currentRegion
->dict
.get());
1730 if (iter
== nullptr) {
1735 tmpSymbol
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
1736 if (tmpSymbol
== nullptr) {
1740 DEBUG_INFO("adding variable %s\n", tmpSymbol
->getCStringNoCopy());
1742 tmpObject
= currentRegion
->dict
->getObject(tmpSymbol
);
1745 ok
= convertObjectToProp(tmpBuffer
, &length
, tmpSymbol
, tmpObject
);
1747 tmpBuffer
+= length
;
1748 maxLength
-= length
;
1753 bcopy(buffer
, currentRegion
->image
, currentRegion
->size
);
1756 IODelete(buffer
, UInt8
, currentRegion
->size
);
1758 sizeUsed
= OSNumber::withNumber(maxLength
, 32);
1759 _nvramController
->setProperty(currentRegion
->name
, sizeUsed
.get());
1762 if ((strncmp(currentRegion
->name
, NVRAM_CHRP_PARTITION_NAME_SYSTEM
, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM
)) == 0) &&
1763 (_systemService
!= nullptr)) {
1764 _systemService
->setProperties(_systemDict
.get());
1765 } else if ((strncmp(currentRegion
->name
, NVRAM_CHRP_PARTITION_NAME_COMMON
, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON
)) == 0) &&
1766 (_commonService
!= nullptr)) {
1767 _commonService
->setProperties(_commonDict
.get());
1771 return kIOReturnBadArgument
;
1775 DEBUG_INFO("ok=%d\n", ok
);
1777 return _nvramController
->write(0, _nvramImage
, _nvramSize
);
1781 IODTNVRAM::getOFVariableType(const char *propName
) const
1787 IODTNVRAM::getOFVariableType(const OSSymbol
*propSymbol
) const
1794 IODTNVRAM::getOFVariablePerm(const char *propName
) const
1800 IODTNVRAM::getOFVariablePerm(const OSSymbol
*propSymbol
) const
1806 IODTNVRAM::getOWVariableInfo(UInt32 variableNumber
, const OSSymbol
**propSymbol
,
1807 UInt32
*propType
, UInt32
*propOffset
)
1813 IODTNVRAM::convertPropToObject(UInt8
*propName
, UInt32 propNameLength
,
1814 UInt8
*propData
, UInt32 propDataLength
,
1815 const OSSymbol
**propSymbol
,
1816 OSObject
**propObject
)
1818 OSSharedPtr
<const OSSymbol
> tmpSymbol
;
1819 OSSharedPtr
<OSNumber
> tmpNumber
;
1820 OSSharedPtr
<OSString
> tmpString
;
1821 OSSharedPtr
<OSObject
> tmpObject
= nullptr;
1823 propName
[propNameLength
] = '\0';
1824 tmpSymbol
= OSSymbol::withCString((const char *)propName
);
1825 propName
[propNameLength
] = '=';
1826 if (tmpSymbol
== nullptr) {
1830 switch (getVariableType(tmpSymbol
.get())) {
1831 case kOFVariableTypeBoolean
:
1832 if (!strncmp("true", (const char *)propData
, propDataLength
)) {
1833 tmpObject
.reset(kOSBooleanTrue
, OSRetain
);
1834 } else if (!strncmp("false", (const char *)propData
, propDataLength
)) {
1835 tmpObject
.reset(kOSBooleanFalse
, OSRetain
);
1839 case kOFVariableTypeNumber
:
1840 tmpNumber
= OSNumber::withNumber(strtol((const char *)propData
, nullptr, 0), 32);
1841 if (tmpNumber
!= nullptr) {
1842 tmpObject
= tmpNumber
;
1846 case kOFVariableTypeString
:
1847 tmpString
= OSString::withCString((const char *)propData
);
1848 if (tmpString
!= nullptr) {
1849 tmpObject
= tmpString
;
1853 case kOFVariableTypeData
:
1854 tmpObject
= unescapeBytesToData(propData
, propDataLength
);
1861 if (tmpObject
== nullptr) {
1866 *propSymbol
= tmpSymbol
.detach();
1867 *propObject
= tmpObject
.detach();
1873 IODTNVRAM::convertPropToObject(UInt8
*propName
, UInt32 propNameLength
,
1874 UInt8
*propData
, UInt32 propDataLength
,
1875 OSSharedPtr
<const OSSymbol
>& propSymbol
,
1876 OSSharedPtr
<OSObject
>& propObject
)
1878 const OSSymbol
* propSymbolRaw
= nullptr;
1879 OSObject
* propObjectRaw
= nullptr;
1880 bool result
= convertPropToObject(propName
, propNameLength
, propData
, propDataLength
,
1881 &propSymbolRaw
, &propObjectRaw
);
1882 propSymbol
.reset(propSymbolRaw
, OSNoRetain
);
1883 propObject
.reset(propObjectRaw
, OSNoRetain
);
1888 IODTNVRAM::convertObjectToProp(UInt8
*buffer
, UInt32
*length
,
1889 const OSSymbol
*propSymbol
, OSObject
*propObject
)
1891 const UInt8
*propName
;
1892 UInt32 propNameLength
, propDataLength
, remaining
;
1893 IONVRAMVariableType propType
;
1894 OSBoolean
*tmpBoolean
= nullptr;
1895 OSNumber
*tmpNumber
= nullptr;
1896 OSString
*tmpString
= nullptr;
1897 OSSharedPtr
<OSData
> tmpData
;
1899 propName
= (const UInt8
*)propSymbol
->getCStringNoCopy();
1900 propNameLength
= propSymbol
->getLength();
1901 propType
= getVariableType(propSymbol
);
1903 // Get the size of the data.
1904 propDataLength
= 0xFFFFFFFF;
1906 case kOFVariableTypeBoolean
:
1907 tmpBoolean
= OSDynamicCast(OSBoolean
, propObject
);
1908 if (tmpBoolean
!= nullptr) {
1913 case kOFVariableTypeNumber
:
1914 tmpNumber
= OSDynamicCast(OSNumber
, propObject
);
1915 if (tmpNumber
!= nullptr) {
1916 propDataLength
= 10;
1920 case kOFVariableTypeString
:
1921 tmpString
= OSDynamicCast(OSString
, propObject
);
1922 if (tmpString
!= nullptr) {
1923 propDataLength
= tmpString
->getLength();
1927 case kOFVariableTypeData
:
1928 tmpData
.reset(OSDynamicCast(OSData
, propObject
), OSNoRetain
);
1929 if (tmpData
!= nullptr) {
1930 tmpData
= escapeDataToData(tmpData
.detach());
1931 propDataLength
= tmpData
->getLength();
1939 // Make sure the propertySize is known and will fit.
1940 if (propDataLength
== 0xFFFFFFFF) {
1943 if ((propNameLength
+ propDataLength
+ 2) > *length
) {
1947 // Copy the property name equal sign.
1948 buffer
+= snprintf((char *)buffer
, *length
, "%s=", propName
);
1949 remaining
= *length
- propNameLength
- 1;
1952 case kOFVariableTypeBoolean
:
1953 if (tmpBoolean
->getValue()) {
1954 strlcpy((char *)buffer
, "true", remaining
);
1956 strlcpy((char *)buffer
, "false", remaining
);
1960 case kOFVariableTypeNumber
:
1962 uint32_t tmpValue
= tmpNumber
->unsigned32BitValue();
1963 if (tmpValue
== 0xFFFFFFFF) {
1964 strlcpy((char *)buffer
, "-1", remaining
);
1965 } else if (tmpValue
< 1000) {
1966 snprintf((char *)buffer
, remaining
, "%d", (uint32_t)tmpValue
);
1968 snprintf((char *)buffer
, remaining
, "0x%x", (uint32_t)tmpValue
);
1973 case kOFVariableTypeString
:
1974 strlcpy((char *)buffer
, tmpString
->getCStringNoCopy(), remaining
);
1977 case kOFVariableTypeData
:
1978 bcopy(tmpData
->getBytesNoCopy(), buffer
, propDataLength
);
1986 propDataLength
= ((UInt32
) strlen((const char *)buffer
));
1988 *length
= propNameLength
+ propDataLength
+ 2;
1995 IODTNVRAM::generateOWChecksum(UInt8
*buffer
)
1997 UInt32 cnt
, checksum
= 0;
1998 UInt16
*tmpBuffer
= (UInt16
*)buffer
;
2000 for (cnt
= 0; cnt
< _commonPartitionSize
/ 2; cnt
++) {
2001 checksum
+= tmpBuffer
[cnt
];
2004 return checksum
% 0x0000FFFF;
2008 IODTNVRAM::validateOWChecksum(UInt8
*buffer
)
2010 UInt32 cnt
, checksum
, sum
= 0;
2011 UInt16
*tmpBuffer
= (UInt16
*)buffer
;
2013 for (cnt
= 0; cnt
< _commonPartitionSize
/ 2; cnt
++) {
2014 sum
+= tmpBuffer
[cnt
];
2017 checksum
= (sum
>> 16) + (sum
& 0x0000FFFF);
2018 if (checksum
== 0x10000) {
2021 checksum
= (checksum
^ 0x0000FFFF) & 0x0000FFFF;
2023 return checksum
== 0;
2027 IODTNVRAM::updateOWBootArgs(const OSSymbol
*key
, OSObject
*value
)
2033 IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor
*hdr
, UInt32
*where
)
2039 IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry
*entry
,
2040 const OSSymbol
**name
,
2043 return kIOReturnUnsupported
;
2048 IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry
*entry
,
2049 const OSSymbol
*name
,
2052 return kIOReturnUnsupported
;
2057 IODTNVRAM::unescapeBytesToData(const UInt8
*bytes
, UInt32 length
)
2059 OSSharedPtr
<OSData
> data
;
2060 UInt32 totalLength
= 0;
2065 // Calculate the actual length of the data.
2068 for (cnt
= 0; cnt
< length
;) {
2069 byte
= bytes
[cnt
++];
2071 byte
= bytes
[cnt
++];
2080 totalLength
+= cnt2
;
2084 // Create an empty OSData of the correct size.
2085 data
= OSData::withCapacity(totalLength
);
2086 if (data
!= nullptr) {
2087 for (cnt
= 0; cnt
< length
;) {
2088 byte
= bytes
[cnt
++];
2090 byte
= bytes
[cnt
++];
2092 byte
= (byte
& 0x80) ? 0xFF : 0x00;
2096 data
->appendByte(byte
, cnt2
);
2105 IODTNVRAM::escapeDataToData(OSData
* value
)
2107 OSSharedPtr
<OSData
> result
;
2108 const UInt8
*startPtr
;
2109 const UInt8
*endPtr
;
2110 const UInt8
*wherePtr
;
2114 wherePtr
= (const UInt8
*) value
->getBytesNoCopy();
2115 endPtr
= wherePtr
+ value
->getLength();
2117 result
= OSData::withCapacity((unsigned int) (endPtr
- wherePtr
));
2122 while (wherePtr
< endPtr
) {
2123 startPtr
= wherePtr
;
2125 if ((byte
== 0x00) || (byte
== 0xFF)) {
2127 ((wherePtr
- startPtr
) < 0x80) && (wherePtr
< endPtr
) && (byte
== *wherePtr
);
2130 ok
&= result
->appendByte(0xff, 1);
2131 byte
= (byte
& 0x80) | ((UInt8
)(wherePtr
- startPtr
));
2133 ok
&= result
->appendByte(byte
, 1);
2135 ok
&= result
->appendByte(0, 1);
2145 IsApplePropertyName(const char * propName
)
2148 while ((c
= *propName
++)) {
2149 if ((c
>= 'A') && (c
<= 'Z')) {
2158 IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry
*entry
,
2159 const OSSymbol
**name
,
2162 IOReturn err
= kIOReturnNoResources
;
2164 const UInt8
*startPtr
;
2165 const UInt8
*endPtr
;
2166 const UInt8
*wherePtr
;
2167 const UInt8
*nvPath
= nullptr;
2168 const char *nvName
= nullptr;
2169 const char *resultName
= nullptr;
2170 const UInt8
*resultValue
= nullptr;
2171 UInt32 resultValueLen
= 0;
2175 data
= OSDynamicCast(OSData
, _commonDict
->getObject(_registryPropertiesKey
.get()));
2178 if (data
== nullptr) {
2182 startPtr
= (const UInt8
*) data
->getBytesNoCopy();
2183 endPtr
= startPtr
+ data
->getLength();
2185 wherePtr
= startPtr
;
2186 while (wherePtr
< endPtr
) {
2187 byte
= *(wherePtr
++);
2192 if (nvPath
== nullptr) {
2194 } else if (nvName
== nullptr) {
2195 nvName
= (const char *) startPtr
;
2197 OSSharedPtr
<IORegistryEntry
> compareEntry
= IORegistryEntry::fromPath((const char *) nvPath
, gIODTPlane
);
2198 if (entry
== compareEntry
) {
2199 bool appleProp
= IsApplePropertyName(nvName
);
2200 if (!appleProp
|| !resultName
) {
2201 resultName
= nvName
;
2202 resultValue
= startPtr
;
2203 resultValueLen
= (UInt32
) (wherePtr
- startPtr
- 1); // OSData getLength() is 32b
2212 startPtr
= wherePtr
;
2215 *name
= OSSymbol::withCString(resultName
).detach();
2216 *value
= unescapeBytesToData(resultValue
, resultValueLen
).detach();
2217 if ((*name
!= nullptr) && (*value
!= nullptr)) {
2218 err
= kIOReturnSuccess
;
2220 err
= kIOReturnNoMemory
;
2227 IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry
*entry
,
2228 const OSSymbol
*propName
,
2231 OSSharedPtr
<OSData
> data
, oldData
;
2232 const UInt8
*startPtr
;
2233 const UInt8
*propStart
;
2234 const UInt8
*endPtr
;
2235 const UInt8
*wherePtr
;
2236 const UInt8
*nvPath
= nullptr;
2237 const char *nvName
= nullptr;
2242 bool settingAppleProp
;
2244 settingAppleProp
= IsApplePropertyName(propName
->getCStringNoCopy());
2246 // copy over existing properties for other entries
2250 oldData
.reset(OSDynamicCast(OSData
, _commonDict
->getObject(_registryPropertiesKey
.get())), OSRetain
);
2252 startPtr
= (const UInt8
*) oldData
->getBytesNoCopy();
2253 endPtr
= startPtr
+ oldData
->getLength();
2255 propStart
= startPtr
;
2256 wherePtr
= startPtr
;
2257 while (wherePtr
< endPtr
) {
2258 byte
= *(wherePtr
++);
2262 if (nvPath
== nullptr) {
2264 } else if (nvName
== nullptr) {
2265 nvName
= (const char *) startPtr
;
2267 OSSharedPtr
<IORegistryEntry
> compareEntry
= IORegistryEntry::fromPath((const char *) nvPath
, gIODTPlane
);
2269 if (entry
== compareEntry
) {
2270 if ((settingAppleProp
&& propName
->isEqualTo(nvName
))
2271 || (!settingAppleProp
&& !IsApplePropertyName(nvName
))) {
2272 // delete old property (nvPath -> wherePtr) source OSData len is 32b
2273 data
= OSData::withBytes(propStart
, (UInt32
)(nvPath
- propStart
));
2275 ok
&= data
->appendBytes(wherePtr
, (UInt32
)(endPtr
- wherePtr
));
2284 startPtr
= wherePtr
;
2288 // make the new property
2292 data
= OSData::withData(oldData
.get());
2294 data
= OSData::withCapacity(16);
2301 if (ok
&& value
&& value
->getLength()) {
2303 // get entries in path
2304 OSSharedPtr
<OSArray
> array
= OSArray::withCapacity(5);
2310 array
->setObject(entry
);
2311 } while ((entry
= entry
->getParentEntry(gIODTPlane
)));
2314 for (int i
= array
->getCount() - 3;
2315 (entry
= (IORegistryEntry
*) array
->getObject(i
));
2317 name
= entry
->getName(gIODTPlane
);
2318 comp
= entry
->getLocation(gIODTPlane
);
2320 ok
&= data
->appendBytes("/@", 2);
2325 ok
&= data
->appendByte('/', 1);
2328 ok
&= data
->appendBytes(comp
, (unsigned int) strnlen(comp
, UINT16_MAX
));
2330 ok
&= data
->appendByte(0, 1);
2332 ok
&= data
->appendBytes(propName
->getCStringNoCopy(), propName
->getLength() + 1);
2334 // append escaped data
2335 OSSharedPtr
<OSData
> escapedData
= escapeDataToData(value
);
2336 ok
&= (escapedData
!= nullptr);
2338 ok
&= data
->appendBytes(escapedData
.get());
2344 ok
= _commonDict
->setObject(_registryPropertiesKey
.get(), data
.get());
2348 if (syncVariables() != kIOReturnSuccess
) {
2350 _commonDict
->setObject(_registryPropertiesKey
.get(), oldData
.get());
2352 _commonDict
->removeObject(_registryPropertiesKey
.get());
2354 (void) syncVariables();
2363 return ok
? kIOReturnSuccess
: kIOReturnNoMemory
;
2367 IODTNVRAM::safeToSync(void)
2373 // delta interval went by
2374 clock_get_uptime(&delta
);
2376 // Figure it in seconds.
2377 absolutetime_to_nanoseconds(delta
, &delta_ns
);
2378 delta_secs
= (SInt32
)(delta_ns
/ NSEC_PER_SEC
);
2380 if ((delta_secs
> (_lastDeviceSync
+ MIN_SYNC_NOW_INTERVAL
)) || _freshInterval
) {
2381 _lastDeviceSync
= delta_secs
;
2382 _freshInterval
= FALSE
;