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 #include <IOKit/IOLib.h>
31 #include <IOKit/IONVRAM.h>
32 #include <IOKit/IOPlatformExpert.h>
33 #include <IOKit/IOUserClient.h>
34 #include <IOKit/IOKitKeys.h>
35 #include <IOKit/IOKitKeysPrivate.h>
36 #include <kern/debug.h>
37 #include <pexpert/pexpert.h>
39 #define super IOService
41 #define kIONVRAMPrivilege kIOClientPrivilegeAdministrator
42 //#define kIONVRAMPrivilege kIOClientPrivilegeLocalUser
44 OSDefineMetaClassAndStructors(IODTNVRAM
, IOService
);
47 IODTNVRAM::init(IORegistryEntry
*old
, const IORegistryPlane
*plane
)
51 if (!super::init(old
, plane
)) {
55 dict
= OSDictionary::withCapacity(1);
59 setPropertyTable(dict
);
61 _nvramImage
= IONew(UInt8
, kIODTNVRAMImageSize
);
62 if (_nvramImage
== 0) {
66 _nvramPartitionOffsets
= OSDictionary::withCapacity(1);
67 if (_nvramPartitionOffsets
== 0) {
71 _nvramPartitionLengths
= OSDictionary::withCapacity(1);
72 if (_nvramPartitionLengths
== 0) {
76 _registryPropertiesKey
= OSSymbol::withCStringNoCopy("aapl,pci");
77 if (_registryPropertiesKey
== 0) {
81 // <rdar://problem/9529235> race condition possible between
82 // IODTNVRAM and IONVRAMController (restore loses boot-args)
89 IODTNVRAM::initProxyData(void)
91 IORegistryEntry
*entry
;
92 const char *key
= "nvram-proxy-data";
97 entry
= IORegistryEntry::fromPath("/chosen", gIODTPlane
);
99 prop
= entry
->getProperty(key
);
101 data
= OSDynamicCast(OSData
, prop
);
103 bytes
= data
->getBytesNoCopy();
104 if ((bytes
!= 0) && (data
->getLength() <= kIODTNVRAMImageSize
)) {
105 bcopy(bytes
, _nvramImage
, data
->getLength());
111 entry
->removeProperty(key
);
117 IODTNVRAM::registerNVRAMController(IONVRAMController
*nvram
)
119 if (_nvramController
!= 0) {
123 _nvramController
= nvram
;
125 // <rdar://problem/9529235> race condition possible between
126 // IODTNVRAM and IONVRAMController (restore loses boot-args)
128 _nvramController
->read(0, _nvramImage
, kIODTNVRAMImageSize
);
132 (void) syncVariables();
133 IOLockUnlock(_ofLock
);
138 IODTNVRAM::initNVRAMImage(void)
140 char partitionID
[18];
141 UInt32 partitionOffset
, partitionLength
;
142 UInt32 freePartitionOffset
, freePartitionSize
;
143 UInt32 currentLength
, currentOffset
= 0;
144 OSNumber
*partitionOffsetNumber
, *partitionLengthNumber
;
146 // Find the offsets for the OF, XPRAM, NameRegistry and PanicInfo partitions.
147 _ofPartitionOffset
= 0xFFFFFFFF;
148 _piPartitionOffset
= 0xFFFFFFFF;
149 freePartitionOffset
= 0xFFFFFFFF;
150 freePartitionSize
= 0;
152 // Look through the partitions to find the OF, MacOS partitions.
153 while (currentOffset
< kIODTNVRAMImageSize
) {
154 currentLength
= ((UInt16
*)(_nvramImage
+ currentOffset
))[1] * 16;
156 if (currentLength
< 16) {
159 partitionOffset
= currentOffset
+ 16;
160 partitionLength
= currentLength
- 16;
161 if ((partitionOffset
+ partitionLength
) > kIODTNVRAMImageSize
) {
165 if (strncmp((const char *)_nvramImage
+ currentOffset
+ 4,
166 kIODTNVRAMOFPartitionName
, 12) == 0) {
167 _ofPartitionOffset
= partitionOffset
;
168 _ofPartitionSize
= partitionLength
;
169 } else if (strncmp((const char *)_nvramImage
+ currentOffset
+ 4,
170 kIODTNVRAMXPRAMPartitionName
, 12) == 0) {
171 } else if (strncmp((const char *)_nvramImage
+ currentOffset
+ 4,
172 kIODTNVRAMPanicInfoPartitonName
, 12) == 0) {
173 _piPartitionOffset
= partitionOffset
;
174 _piPartitionSize
= partitionLength
;
175 } else if (strncmp((const char *)_nvramImage
+ currentOffset
+ 4,
176 kIODTNVRAMFreePartitionName
, 12) == 0) {
177 freePartitionOffset
= currentOffset
;
178 freePartitionSize
= currentLength
;
180 // Construct the partition ID from the signature and name.
181 snprintf(partitionID
, sizeof(partitionID
), "0x%02x,",
182 *(UInt8
*)(_nvramImage
+ currentOffset
));
183 strncpy(partitionID
+ 5,
184 (const char *)(_nvramImage
+ currentOffset
+ 4), 12);
185 partitionID
[17] = '\0';
187 partitionOffsetNumber
= OSNumber::withNumber(partitionOffset
, 32);
188 partitionLengthNumber
= OSNumber::withNumber(partitionLength
, 32);
190 // Save the partition offset and length
191 _nvramPartitionOffsets
->setObject(partitionID
, partitionOffsetNumber
);
192 _nvramPartitionLengths
->setObject(partitionID
, partitionLengthNumber
);
194 partitionOffsetNumber
->release();
195 partitionLengthNumber
->release();
197 currentOffset
+= currentLength
;
200 if (_ofPartitionOffset
!= 0xFFFFFFFF) {
201 _ofImage
= _nvramImage
+ _ofPartitionOffset
;
204 if (_piPartitionOffset
== 0xFFFFFFFF) {
205 if (freePartitionSize
> 0x20) {
206 // Set the signature to 0xa1.
207 _nvramImage
[freePartitionOffset
] = 0xa1;
208 // Set the checksum to 0.
209 _nvramImage
[freePartitionOffset
+ 1] = 0;
210 // Set the name for the Panic Info partition.
211 strncpy((char *)(_nvramImage
+ freePartitionOffset
+ 4),
212 kIODTNVRAMPanicInfoPartitonName
, 12);
214 // Calculate the partition offset and size.
215 _piPartitionOffset
= freePartitionOffset
+ 0x10;
216 _piPartitionSize
= 0x800;
217 if (_piPartitionSize
+ 0x20 > freePartitionSize
) {
218 _piPartitionSize
= freePartitionSize
- 0x20;
221 _piImage
= _nvramImage
+ _piPartitionOffset
;
223 // Zero the new partition.
224 bzero(_piImage
, _piPartitionSize
);
226 // Set the partition size.
227 *(UInt16
*)(_nvramImage
+ freePartitionOffset
+ 2) =
228 (_piPartitionSize
/ 0x10) + 1;
230 // Set the partition checksum.
231 _nvramImage
[freePartitionOffset
+ 1] =
232 calculatePartitionChecksum(_nvramImage
+ freePartitionOffset
);
234 // Calculate the free partition offset and size.
235 freePartitionOffset
+= _piPartitionSize
+ 0x10;
236 freePartitionSize
-= _piPartitionSize
+ 0x10;
238 // Set the signature to 0x7f.
239 _nvramImage
[freePartitionOffset
] = 0x7f;
240 // Set the checksum to 0.
241 _nvramImage
[freePartitionOffset
+ 1] = 0;
242 // Set the name for the free partition.
243 strncpy((char *)(_nvramImage
+ freePartitionOffset
+ 4),
244 kIODTNVRAMFreePartitionName
, 12);
245 // Set the partition size.
246 *(UInt16
*)(_nvramImage
+ freePartitionOffset
+ 2) =
247 freePartitionSize
/ 0x10;
248 // Set the partition checksum.
249 _nvramImage
[freePartitionOffset
+ 1] =
250 calculatePartitionChecksum(_nvramImage
+ freePartitionOffset
);
252 if (_nvramController
!= 0) {
253 _nvramController
->write(0, _nvramImage
, kIODTNVRAMImageSize
);
257 _piImage
= _nvramImage
+ _piPartitionOffset
;
261 _freshInterval
= TRUE
; // we will allow sync() even before the first 15 minutes have passed.
267 IODTNVRAM::syncInternal(bool rateLimit
)
269 // Don't try to perform controller operations if none has been registered.
270 if (_nvramController
== 0) {
274 // Rate limit requests to sync. Drivers that need this rate limiting will
275 // shadow the data and only write to flash when they get a sync call
276 if (rateLimit
&& !safeToSync()) {
280 _nvramController
->sync();
284 IODTNVRAM::sync(void)
290 IODTNVRAM::serializeProperties(OSSerialize
*s
) const
292 bool result
, hasPrivilege
;
296 OSCollectionIterator
*iter
= 0;
298 // Verify permissions.
299 hasPrivilege
= (kIOReturnSuccess
== IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege
));
302 /* No nvram. Return an empty dictionary. */
303 dict
= OSDictionary::withCapacity(1);
309 dict
= OSDictionary::withDictionary(_ofDict
);
310 IOLockUnlock(_ofLock
);
315 /* Copy properties with client privilege. */
316 iter
= OSCollectionIterator::withCollection(dict
);
322 key
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
327 variablePerm
= getOFVariablePerm(key
);
328 if ((hasPrivilege
|| (variablePerm
!= kOFVariablePermRootOnly
)) &&
329 (!(variablePerm
== kOFVariablePermKernelOnly
&& current_task() != kernel_task
))) {
331 dict
->removeObject(key
);
337 result
= dict
->serialize(s
);
348 IODTNVRAM::copyProperty(const OSSymbol
*aKey
) const
358 // Verify permissions.
359 variablePerm
= getOFVariablePerm(aKey
);
360 result
= IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege
);
361 if (result
!= kIOReturnSuccess
) {
362 if (variablePerm
== kOFVariablePermRootOnly
) {
366 if (variablePerm
== kOFVariablePermKernelOnly
&& current_task() != kernel_task
) {
371 theObject
= _ofDict
->getObject(aKey
);
375 IOLockUnlock(_ofLock
);
381 IODTNVRAM::copyProperty(const char *aKey
) const
383 const OSSymbol
*keySymbol
;
384 OSObject
*theObject
= 0;
386 keySymbol
= OSSymbol::withCString(aKey
);
387 if (keySymbol
!= 0) {
388 theObject
= copyProperty(keySymbol
);
389 keySymbol
->release();
396 IODTNVRAM::getProperty(const OSSymbol
*aKey
) const
400 theObject
= copyProperty(aKey
);
402 theObject
->release();
409 IODTNVRAM::getProperty(const char *aKey
) const
413 theObject
= copyProperty(aKey
);
415 theObject
->release();
422 IODTNVRAM::setProperty(const OSSymbol
*aKey
, OSObject
*anObject
)
425 UInt32 propType
, propPerm
;
426 OSString
*tmpString
= 0;
427 OSObject
*propObject
= 0, *oldObject
;
433 // Verify permissions.
434 propPerm
= getOFVariablePerm(aKey
);
435 if (IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege
) != kIOReturnSuccess
) {
436 if (propPerm
!= kOFVariablePermUserWrite
) {
440 if (propPerm
== kOFVariablePermKernelOnly
&& current_task() != kernel_task
) {
444 // Don't allow change of 'aapl,panic-info'.
445 if (aKey
->isEqualTo(kIODTNVRAMPanicInfoKey
)) {
449 // Make sure the object is of the correct type.
450 propType
= getOFVariableType(aKey
);
452 case kOFVariableTypeBoolean
:
453 propObject
= OSDynamicCast(OSBoolean
, anObject
);
456 case kOFVariableTypeNumber
:
457 propObject
= OSDynamicCast(OSNumber
, anObject
);
460 case kOFVariableTypeString
:
461 propObject
= OSDynamicCast(OSString
, anObject
);
464 case kOFVariableTypeData
:
465 propObject
= OSDynamicCast(OSData
, anObject
);
466 if (propObject
== 0) {
467 tmpString
= OSDynamicCast(OSString
, anObject
);
468 if (tmpString
!= 0) {
469 propObject
= OSData::withBytes(tmpString
->getCStringNoCopy(),
470 tmpString
->getLength());
476 if (propObject
== 0) {
482 oldObject
= _ofDict
->getObject(aKey
);
486 result
= _ofDict
->setObject(aKey
, propObject
);
489 if (syncVariables() != kIOReturnSuccess
) {
491 _ofDict
->setObject(aKey
, oldObject
);
493 _ofDict
->removeObject(aKey
);
495 (void) syncVariables();
501 oldObject
->release();
504 propObject
->release();
507 IOLockUnlock(_ofLock
);
513 IODTNVRAM::removeProperty(const OSSymbol
*aKey
)
522 // Verify permissions.
523 propPerm
= getOFVariablePerm(aKey
);
524 result
= IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator
);
525 if (result
!= kIOReturnSuccess
) {
526 if (propPerm
!= kOFVariablePermUserWrite
) {
530 if (propPerm
== kOFVariablePermKernelOnly
&& current_task() != kernel_task
) {
534 // Don't allow change of 'aapl,panic-info'.
535 if (aKey
->isEqualTo(kIODTNVRAMPanicInfoKey
)) {
539 // If the object exists, remove it from the dictionary.
542 result
= _ofDict
->getObject(aKey
) != 0;
544 _ofDict
->removeObject(aKey
);
548 (void) syncVariables();
551 IOLockUnlock(_ofLock
);
555 IODTNVRAM::setProperties(OSObject
*properties
)
560 const OSString
*tmpStr
;
562 OSCollectionIterator
*iter
;
564 dict
= OSDynamicCast(OSDictionary
, properties
);
566 return kIOReturnBadArgument
;
569 iter
= OSCollectionIterator::withCollection(dict
);
571 return kIOReturnBadArgument
;
575 key
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
580 object
= dict
->getObject(key
);
585 if (key
->isEqualTo(kIONVRAMDeletePropertyKey
)) {
586 tmpStr
= OSDynamicCast(OSString
, object
);
588 key
= OSSymbol::withString(tmpStr
);
595 } else if (key
->isEqualTo(kIONVRAMSyncNowPropertyKey
) || key
->isEqualTo(kIONVRAMForceSyncNowPropertyKey
)) {
596 tmpStr
= OSDynamicCast(OSString
, object
);
600 // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
602 syncInternal(key
->isEqualTo(kIONVRAMSyncNowPropertyKey
));
607 result
= setProperty(key
, object
);
614 return kIOReturnSuccess
;
616 return kIOReturnError
;
621 IODTNVRAM::readXPRAM(IOByteCount offset
, UInt8
*buffer
,
624 return kIOReturnUnsupported
;
628 IODTNVRAM::writeXPRAM(IOByteCount offset
, UInt8
*buffer
,
631 return kIOReturnUnsupported
;
635 IODTNVRAM::readNVRAMProperty(IORegistryEntry
*entry
,
636 const OSSymbol
**name
,
641 err
= readNVRAMPropertyType1(entry
, name
, value
);
647 IODTNVRAM::writeNVRAMProperty(IORegistryEntry
*entry
,
648 const OSSymbol
*name
,
653 err
= writeNVRAMPropertyType1(entry
, name
, value
);
659 IODTNVRAM::getNVRAMPartitions(void)
661 return _nvramPartitionLengths
;
665 IODTNVRAM::readNVRAMPartition(const OSSymbol
*partitionID
,
666 IOByteCount offset
, UInt8
*buffer
,
669 OSNumber
*partitionOffsetNumber
, *partitionLengthNumber
;
670 UInt32 partitionOffset
, partitionLength
, end
;
672 partitionOffsetNumber
=
673 (OSNumber
*)_nvramPartitionOffsets
->getObject(partitionID
);
674 partitionLengthNumber
=
675 (OSNumber
*)_nvramPartitionLengths
->getObject(partitionID
);
677 if ((partitionOffsetNumber
== 0) || (partitionLengthNumber
== 0)) {
678 return kIOReturnNotFound
;
681 partitionOffset
= partitionOffsetNumber
->unsigned32BitValue();
682 partitionLength
= partitionLengthNumber
->unsigned32BitValue();
684 if (os_add_overflow(offset
, length
, &end
)) {
685 return kIOReturnBadArgument
;
687 if ((buffer
== 0) || (length
== 0) || (end
> partitionLength
)) {
688 return kIOReturnBadArgument
;
691 bcopy(_nvramImage
+ partitionOffset
+ offset
, buffer
, length
);
693 return kIOReturnSuccess
;
697 IODTNVRAM::writeNVRAMPartition(const OSSymbol
*partitionID
,
698 IOByteCount offset
, UInt8
*buffer
,
701 OSNumber
*partitionOffsetNumber
, *partitionLengthNumber
;
702 UInt32 partitionOffset
, partitionLength
, end
;
704 partitionOffsetNumber
=
705 (OSNumber
*)_nvramPartitionOffsets
->getObject(partitionID
);
706 partitionLengthNumber
=
707 (OSNumber
*)_nvramPartitionLengths
->getObject(partitionID
);
709 if ((partitionOffsetNumber
== 0) || (partitionLengthNumber
== 0)) {
710 return kIOReturnNotFound
;
713 partitionOffset
= partitionOffsetNumber
->unsigned32BitValue();
714 partitionLength
= partitionLengthNumber
->unsigned32BitValue();
716 if (os_add_overflow(offset
, length
, &end
)) {
717 return kIOReturnBadArgument
;
719 if ((buffer
== 0) || (length
== 0) || (end
> partitionLength
)) {
720 return kIOReturnBadArgument
;
723 bcopy(buffer
, _nvramImage
+ partitionOffset
+ offset
, length
);
725 if (_nvramController
!= 0) {
726 _nvramController
->write(0, _nvramImage
, kIODTNVRAMImageSize
);
729 return kIOReturnSuccess
;
733 IODTNVRAM::savePanicInfo(UInt8
*buffer
, IOByteCount length
)
735 if ((_piImage
== 0) || (length
<= 0)) {
739 if (length
> (_piPartitionSize
- 4)) {
740 length
= _piPartitionSize
- 4;
743 // Save the Panic Info.
744 bcopy(buffer
, _piImage
+ 4, length
);
746 // Save the Panic Info length.
747 *(UInt32
*)_piImage
= length
;
749 if (_nvramController
!= 0) {
750 _nvramController
->write(0, _nvramImage
, kIODTNVRAMImageSize
);
753 * This prevents OF variables from being committed if the system has panicked
755 _systemPaniced
= true;
756 /* The call to sync() forces the NVRAM controller to write the panic info
757 * partition to NVRAM.
767 IODTNVRAM::calculatePartitionChecksum(UInt8
*partitionHeader
)
769 UInt8 cnt
, isum
, csum
= 0;
771 for (cnt
= 0; cnt
< 0x10; cnt
++) {
772 isum
= csum
+ partitionHeader
[cnt
];
783 IODTNVRAM::initOFVariables(void)
786 UInt8
*propName
, *propData
;
787 UInt32 propNameLength
, propDataLength
;
788 const OSSymbol
*propSymbol
;
789 OSObject
*propObject
;
792 return kIOReturnNotReady
;
795 _ofDict
= OSDictionary::withCapacity(1);
796 _ofLock
= IOLockAlloc();
797 if (!_ofDict
|| !_ofLock
) {
798 return kIOReturnNoMemory
;
802 while (cnt
< _ofPartitionSize
) {
803 // Break if there is no name.
804 if (_ofImage
[cnt
] == '\0') {
808 // Find the length of the name.
809 propName
= _ofImage
+ cnt
;
810 for (propNameLength
= 0; (cnt
+ propNameLength
) < _ofPartitionSize
;
812 if (_ofImage
[cnt
+ propNameLength
] == '=') {
817 // Break if the name goes past the end of the partition.
818 if ((cnt
+ propNameLength
) >= _ofPartitionSize
) {
821 cnt
+= propNameLength
+ 1;
823 propData
= _ofImage
+ cnt
;
824 for (propDataLength
= 0; (cnt
+ propDataLength
) < _ofPartitionSize
;
826 if (_ofImage
[cnt
+ propDataLength
] == '\0') {
831 // Break if the data goes past the end of the partition.
832 if ((cnt
+ propDataLength
) >= _ofPartitionSize
) {
835 cnt
+= propDataLength
+ 1;
837 if (convertPropToObject(propName
, propNameLength
,
838 propData
, propDataLength
,
839 &propSymbol
, &propObject
)) {
840 _ofDict
->setObject(propSymbol
, propObject
);
841 propSymbol
->release();
842 propObject
->release();
846 // Create the boot-args property if it is not in the dictionary.
847 if (_ofDict
->getObject("boot-args") == 0) {
848 propObject
= OSString::withCStringNoCopy("");
849 if (propObject
!= 0) {
850 _ofDict
->setObject("boot-args", propObject
);
851 propObject
->release();
856 propDataLength
= *(UInt32
*)_piImage
;
857 if ((propDataLength
!= 0) && (propDataLength
<= (_piPartitionSize
- 4))) {
858 propObject
= OSData::withBytes(_piImage
+ 4, propDataLength
);
859 _ofDict
->setObject(kIODTNVRAMPanicInfoKey
, propObject
);
860 propObject
->release();
862 // Clear the length from _piImage and mark dirty.
863 *(UInt32
*)_piImage
= 0;
864 if (_nvramController
!= 0) {
865 _nvramController
->write(0, _nvramImage
, kIODTNVRAMImageSize
);
870 return kIOReturnSuccess
;
874 IODTNVRAM::syncOFVariables(void)
876 return kIOReturnUnsupported
;
880 IODTNVRAM::syncVariables(void)
883 UInt32 length
, maxLength
;
884 UInt8
*buffer
, *tmpBuffer
;
885 const OSSymbol
*tmpSymbol
;
887 OSCollectionIterator
*iter
;
889 IOLockAssert(_ofLock
, kIOLockAssertOwned
);
891 if ((_ofImage
== 0) || (_ofDict
== 0) || _systemPaniced
) {
892 return kIOReturnNotReady
;
895 buffer
= tmpBuffer
= IONew(UInt8
, _ofPartitionSize
);
897 return kIOReturnNoMemory
;
899 bzero(buffer
, _ofPartitionSize
);
902 maxLength
= _ofPartitionSize
;
904 iter
= OSCollectionIterator::withCollection(_ofDict
);
910 tmpSymbol
= OSDynamicCast(OSSymbol
, iter
->getNextObject());
911 if (tmpSymbol
== 0) {
915 // Don't save 'aapl,panic-info'.
916 if (tmpSymbol
->isEqualTo(kIODTNVRAMPanicInfoKey
)) {
920 tmpObject
= _ofDict
->getObject(tmpSymbol
);
923 ok
= convertObjectToProp(tmpBuffer
, &length
, tmpSymbol
, tmpObject
);
932 bcopy(buffer
, _ofImage
, _ofPartitionSize
);
935 IODelete(buffer
, UInt8
, _ofPartitionSize
);
938 return kIOReturnBadArgument
;
941 if (_nvramController
!= 0) {
942 return _nvramController
->write(0, _nvramImage
, kIODTNVRAMImageSize
);
945 return kIOReturnNotReady
;
949 const char *variableName
;
952 SInt32 variableOffset
;
954 typedef struct OFVariable OFVariable
;
957 kOWVariableOffsetNumber
= 8,
958 kOWVariableOffsetString
= 17
962 OFVariable gOFVariables
[] = {
963 {"little-endian?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 0},
964 {"real-mode?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 1},
965 {"auto-boot?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 2},
966 {"diag-switch?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 3},
967 {"fcode-debug?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 4},
968 {"oem-banner?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 5},
969 {"oem-logo?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 6},
970 {"use-nvramrc?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, 7},
971 {"use-generic?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, -1},
972 {"default-mac-address?", kOFVariableTypeBoolean
, kOFVariablePermUserRead
, -1},
973 {"real-base", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 8},
974 {"real-size", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 9},
975 {"virt-base", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 10},
976 {"virt-size", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 11},
977 {"load-base", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 12},
978 {"pci-probe-list", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 13},
979 {"pci-probe-mask", kOFVariableTypeNumber
, kOFVariablePermUserRead
, -1},
980 {"screen-#columns", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 14},
981 {"screen-#rows", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 15},
982 {"selftest-#megs", kOFVariableTypeNumber
, kOFVariablePermUserRead
, 16},
983 {"boot-device", kOFVariableTypeString
, kOFVariablePermUserRead
, 17},
984 {"boot-file", kOFVariableTypeString
, kOFVariablePermUserRead
, 18},
985 {"boot-screen", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
986 {"console-screen", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
987 {"diag-device", kOFVariableTypeString
, kOFVariablePermUserRead
, 19},
988 {"diag-file", kOFVariableTypeString
, kOFVariablePermUserRead
, 20},
989 {"input-device", kOFVariableTypeString
, kOFVariablePermUserRead
, 21},
990 {"output-device", kOFVariableTypeString
, kOFVariablePermUserRead
, 22},
991 {"input-device-1", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
992 {"output-device-1", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
993 {"mouse-device", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
994 {"oem-banner", kOFVariableTypeString
, kOFVariablePermUserRead
, 23},
995 {"oem-logo", kOFVariableTypeString
, kOFVariablePermUserRead
, 24},
996 {"nvramrc", kOFVariableTypeString
, kOFVariablePermUserRead
, 25},
997 {"boot-command", kOFVariableTypeString
, kOFVariablePermUserRead
, 26},
998 {"default-client-ip", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
999 {"default-server-ip", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
1000 {"default-gateway-ip", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
1001 {"default-subnet-mask", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
1002 {"default-router-ip", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
1003 {"boot-script", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
1004 {"boot-args", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
1005 {"aapl,pci", kOFVariableTypeData
, kOFVariablePermRootOnly
, -1},
1006 {"security-mode", kOFVariableTypeString
, kOFVariablePermUserRead
, -1},
1007 {"security-password", kOFVariableTypeData
, kOFVariablePermRootOnly
, -1},
1008 {"boot-image", kOFVariableTypeData
, kOFVariablePermUserWrite
, -1},
1009 {"com.apple.System.fp-state", kOFVariableTypeData
, kOFVariablePermKernelOnly
, -1},
1011 {"backlight-level", kOFVariableTypeData
, kOFVariablePermUserWrite
, -1},
1012 {"com.apple.System.sep.art", kOFVariableTypeData
, kOFVariablePermKernelOnly
, -1},
1013 {"com.apple.System.boot-nonce", kOFVariableTypeString
, kOFVariablePermKernelOnly
, -1},
1014 {"darkboot", kOFVariableTypeBoolean
, kOFVariablePermUserWrite
, -1},
1015 {"acc-mb-ld-lifetime", kOFVariableTypeNumber
, kOFVariablePermKernelOnly
, -1},
1016 {"acc-cm-override-charger-count", kOFVariableTypeNumber
, kOFVariablePermKernelOnly
, -1},
1017 {"acc-cm-override-count", kOFVariableTypeNumber
, kOFVariablePermKernelOnly
, -1},
1018 {"enter-tdm-mode", kOFVariableTypeBoolean
, kOFVariablePermUserWrite
, -1},
1019 {"nonce-seeds", kOFVariableTypeData
, kOFVariablePermKernelOnly
, -1},
1021 {0, kOFVariableTypeData
, kOFVariablePermUserRead
, -1}
1025 IODTNVRAM::getOFVariableType(const OSSymbol
*propSymbol
) const
1027 const OFVariable
*ofVar
;
1029 ofVar
= gOFVariables
;
1031 if ((ofVar
->variableName
== 0) ||
1032 propSymbol
->isEqualTo(ofVar
->variableName
)) {
1038 return ofVar
->variableType
;
1042 IODTNVRAM::getOFVariablePerm(const OSSymbol
*propSymbol
) const
1044 const OFVariable
*ofVar
;
1046 ofVar
= gOFVariables
;
1048 if ((ofVar
->variableName
== 0) ||
1049 propSymbol
->isEqualTo(ofVar
->variableName
)) {
1055 return ofVar
->variablePerm
;
1059 IODTNVRAM::getOWVariableInfo(UInt32 variableNumber
, const OSSymbol
**propSymbol
,
1060 UInt32
*propType
, UInt32
*propOffset
)
1062 const OFVariable
*ofVar
;
1064 ofVar
= gOFVariables
;
1066 if (ofVar
->variableName
== 0) {
1070 if (ofVar
->variableOffset
== (SInt32
) variableNumber
) {
1077 *propSymbol
= OSSymbol::withCStringNoCopy(ofVar
->variableName
);
1078 *propType
= ofVar
->variableType
;
1080 switch (*propType
) {
1081 case kOFVariableTypeBoolean
:
1082 *propOffset
= 1 << (31 - variableNumber
);
1085 case kOFVariableTypeNumber
:
1086 *propOffset
= variableNumber
- kOWVariableOffsetNumber
;
1089 case kOFVariableTypeString
:
1090 *propOffset
= variableNumber
- kOWVariableOffsetString
;
1098 IODTNVRAM::convertPropToObject(UInt8
*propName
, UInt32 propNameLength
,
1099 UInt8
*propData
, UInt32 propDataLength
,
1100 const OSSymbol
**propSymbol
,
1101 OSObject
**propObject
)
1104 const OSSymbol
*tmpSymbol
;
1105 OSObject
*tmpObject
;
1106 OSNumber
*tmpNumber
;
1107 OSString
*tmpString
;
1109 // Create the symbol.
1110 propName
[propNameLength
] = '\0';
1111 tmpSymbol
= OSSymbol::withCString((const char *)propName
);
1112 propName
[propNameLength
] = '=';
1113 if (tmpSymbol
== 0) {
1117 propType
= getOFVariableType(tmpSymbol
);
1119 // Create the object.
1122 case kOFVariableTypeBoolean
:
1123 if (!strncmp("true", (const char *)propData
, propDataLength
)) {
1124 tmpObject
= kOSBooleanTrue
;
1125 } else if (!strncmp("false", (const char *)propData
, propDataLength
)) {
1126 tmpObject
= kOSBooleanFalse
;
1130 case kOFVariableTypeNumber
:
1131 tmpNumber
= OSNumber::withNumber(strtol((const char *)propData
, 0, 0), 32);
1132 if (tmpNumber
!= 0) {
1133 tmpObject
= tmpNumber
;
1137 case kOFVariableTypeString
:
1138 tmpString
= OSString::withCString((const char *)propData
);
1139 if (tmpString
!= 0) {
1140 tmpObject
= tmpString
;
1144 case kOFVariableTypeData
:
1145 tmpObject
= unescapeBytesToData(propData
, propDataLength
);
1149 if (tmpObject
== 0) {
1150 tmpSymbol
->release();
1154 *propSymbol
= tmpSymbol
;
1155 *propObject
= tmpObject
;
1161 IODTNVRAM::convertObjectToProp(UInt8
*buffer
, UInt32
*length
,
1162 const OSSymbol
*propSymbol
, OSObject
*propObject
)
1164 const UInt8
*propName
;
1165 UInt32 propNameLength
, propDataLength
, remaining
;
1166 UInt32 propType
, tmpValue
;
1167 OSBoolean
*tmpBoolean
= 0;
1168 OSNumber
*tmpNumber
= 0;
1169 OSString
*tmpString
= 0;
1170 OSData
*tmpData
= 0;
1172 propName
= (const UInt8
*)propSymbol
->getCStringNoCopy();
1173 propNameLength
= propSymbol
->getLength();
1174 propType
= getOFVariableType(propSymbol
);
1176 // Get the size of the data.
1177 propDataLength
= 0xFFFFFFFF;
1179 case kOFVariableTypeBoolean
:
1180 tmpBoolean
= OSDynamicCast(OSBoolean
, propObject
);
1181 if (tmpBoolean
!= 0) {
1186 case kOFVariableTypeNumber
:
1187 tmpNumber
= OSDynamicCast(OSNumber
, propObject
);
1188 if (tmpNumber
!= 0) {
1189 propDataLength
= 10;
1193 case kOFVariableTypeString
:
1194 tmpString
= OSDynamicCast(OSString
, propObject
);
1195 if (tmpString
!= 0) {
1196 propDataLength
= tmpString
->getLength();
1200 case kOFVariableTypeData
:
1201 tmpData
= OSDynamicCast(OSData
, propObject
);
1203 tmpData
= escapeDataToData(tmpData
);
1204 propDataLength
= tmpData
->getLength();
1209 // Make sure the propertySize is known and will fit.
1210 if (propDataLength
== 0xFFFFFFFF) {
1213 if ((propNameLength
+ propDataLength
+ 2) > *length
) {
1217 // Copy the property name equal sign.
1218 buffer
+= snprintf((char *)buffer
, *length
, "%s=", propName
);
1219 remaining
= *length
- propNameLength
- 1;
1222 case kOFVariableTypeBoolean
:
1223 if (tmpBoolean
->getValue()) {
1224 strlcpy((char *)buffer
, "true", remaining
);
1226 strlcpy((char *)buffer
, "false", remaining
);
1230 case kOFVariableTypeNumber
:
1231 tmpValue
= tmpNumber
->unsigned32BitValue();
1232 if (tmpValue
== 0xFFFFFFFF) {
1233 strlcpy((char *)buffer
, "-1", remaining
);
1234 } else if (tmpValue
< 1000) {
1235 snprintf((char *)buffer
, remaining
, "%d", (uint32_t)tmpValue
);
1237 snprintf((char *)buffer
, remaining
, "0x%x", (uint32_t)tmpValue
);
1241 case kOFVariableTypeString
:
1242 strlcpy((char *)buffer
, tmpString
->getCStringNoCopy(), remaining
);
1245 case kOFVariableTypeData
:
1246 bcopy(tmpData
->getBytesNoCopy(), buffer
, propDataLength
);
1251 propDataLength
= strlen((const char *)buffer
);
1253 *length
= propNameLength
+ propDataLength
+ 2;
1260 IODTNVRAM::generateOWChecksum(UInt8
*buffer
)
1262 UInt32 cnt
, checksum
= 0;
1263 UInt16
*tmpBuffer
= (UInt16
*)buffer
;
1265 for (cnt
= 0; cnt
< _ofPartitionSize
/ 2; cnt
++) {
1266 checksum
+= tmpBuffer
[cnt
];
1269 return checksum
% 0x0000FFFF;
1273 IODTNVRAM::validateOWChecksum(UInt8
*buffer
)
1275 UInt32 cnt
, checksum
, sum
= 0;
1276 UInt16
*tmpBuffer
= (UInt16
*)buffer
;
1278 for (cnt
= 0; cnt
< _ofPartitionSize
/ 2; cnt
++) {
1279 sum
+= tmpBuffer
[cnt
];
1282 checksum
= (sum
>> 16) + (sum
& 0x0000FFFF);
1283 if (checksum
== 0x10000) {
1286 checksum
= (checksum
^ 0x0000FFFF) & 0x0000FFFF;
1288 return checksum
== 0;
1292 IODTNVRAM::updateOWBootArgs(const OSSymbol
*key
, OSObject
*value
)
1294 bool wasBootArgs
, bootr
= false;
1296 OSString
*tmpString
, *bootCommand
, *bootArgs
= 0;
1297 const UInt8
*bootCommandData
, *bootArgsData
;
1299 UInt32 bootCommandDataLength
, bootArgsDataLength
, tmpDataLength
;
1301 tmpString
= OSDynamicCast(OSString
, value
);
1302 if (tmpString
== 0) {
1306 if (key
->isEqualTo("boot-command")) {
1307 wasBootArgs
= false;
1308 bootCommand
= tmpString
;
1309 } else if (key
->isEqualTo("boot-args")) {
1311 bootArgs
= tmpString
;
1312 bootCommand
= OSDynamicCast(OSString
, _ofDict
->getObject("boot-command"));
1313 if (bootCommand
== 0) {
1320 bootCommandData
= (const UInt8
*)bootCommand
->getCStringNoCopy();
1321 bootCommandDataLength
= bootCommand
->getLength();
1323 if (bootCommandData
== 0) {
1327 for (cnt
= 0; cnt
< bootCommandDataLength
; cnt
++) {
1328 if ((bootCommandData
[cnt
] == 'b') &&
1329 !strncmp("bootr", (const char *)bootCommandData
+ cnt
, 5)) {
1331 while (bootCommandData
[cnt
] == ' ') {
1339 _ofDict
->removeObject("boot-args");
1344 bootArgsData
= (const UInt8
*)bootArgs
->getCStringNoCopy();
1345 bootArgsDataLength
= bootArgs
->getLength();
1346 if (bootArgsData
== 0) {
1350 tmpDataLength
= cnt
+ bootArgsDataLength
;
1351 tmpData
= IONew(UInt8
, tmpDataLength
+ 1);
1356 cnt
-= strlcpy((char *)tmpData
, (const char *)bootCommandData
, cnt
);
1357 strlcat((char *)tmpData
, (const char *)bootArgsData
, cnt
);
1359 bootCommand
= OSString::withCString((const char *)tmpData
);
1360 if (bootCommand
!= 0) {
1361 _ofDict
->setObject("boot-command", bootCommand
);
1362 bootCommand
->release();
1365 IODelete(tmpData
, UInt8
, tmpDataLength
+ 1);
1367 bootArgs
= OSString::withCString((const char *)(bootCommandData
+ cnt
));
1368 if (bootArgs
!= 0) {
1369 _ofDict
->setObject("boot-args", bootArgs
);
1370 bootArgs
->release();
1376 IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor
*hdr
, UInt32
*where
)
1382 IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry
*entry
,
1383 const OSSymbol
**name
,
1386 return kIOReturnUnsupported
;
1391 IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry
*entry
,
1392 const OSSymbol
*name
,
1395 return kIOReturnUnsupported
;
1400 IODTNVRAM::unescapeBytesToData(const UInt8
*bytes
, UInt32 length
)
1403 UInt32 totalLength
= 0;
1408 // Calculate the actual length of the data.
1411 for (cnt
= 0; cnt
< length
;) {
1412 byte
= bytes
[cnt
++];
1414 byte
= bytes
[cnt
++];
1423 totalLength
+= cnt2
;
1427 // Create an empty OSData of the correct size.
1428 data
= OSData::withCapacity(totalLength
);
1430 for (cnt
= 0; cnt
< length
;) {
1431 byte
= bytes
[cnt
++];
1433 byte
= bytes
[cnt
++];
1435 byte
= (byte
& 0x80) ? 0xFF : 0x00;
1439 data
->appendByte(byte
, cnt2
);
1448 IODTNVRAM::escapeDataToData(OSData
* value
)
1451 const UInt8
* startPtr
;
1452 const UInt8
* endPtr
;
1453 const UInt8
* wherePtr
;
1457 wherePtr
= (const UInt8
*) value
->getBytesNoCopy();
1458 endPtr
= wherePtr
+ value
->getLength();
1460 result
= OSData::withCapacity(endPtr
- wherePtr
);
1465 while (wherePtr
< endPtr
) {
1466 startPtr
= wherePtr
;
1468 if ((byte
== 0x00) || (byte
== 0xFF)) {
1470 ((wherePtr
- startPtr
) < 0x80) && (wherePtr
< endPtr
) && (byte
== *wherePtr
);
1473 ok
&= result
->appendByte(0xff, 1);
1474 byte
= (byte
& 0x80) | (wherePtr
- startPtr
);
1476 ok
&= result
->appendByte(byte
, 1);
1478 ok
&= result
->appendByte(0, 1);
1489 IsApplePropertyName(const char * propName
)
1492 while ((c
= *propName
++)) {
1493 if ((c
>= 'A') && (c
<= 'Z')) {
1502 IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry
*entry
,
1503 const OSSymbol
**name
,
1506 IOReturn err
= kIOReturnNoResources
;
1508 const UInt8
*startPtr
;
1509 const UInt8
*endPtr
;
1510 const UInt8
*wherePtr
;
1511 const UInt8
*nvPath
= 0;
1512 const char *nvName
= 0;
1513 const char *resultName
= 0;
1514 const UInt8
*resultValue
= 0;
1515 UInt32 resultValueLen
= 0;
1522 IOLockLock(_ofLock
);
1523 data
= OSDynamicCast(OSData
, _ofDict
->getObject(_registryPropertiesKey
));
1524 IOLockUnlock(_ofLock
);
1530 startPtr
= (const UInt8
*) data
->getBytesNoCopy();
1531 endPtr
= startPtr
+ data
->getLength();
1533 wherePtr
= startPtr
;
1534 while (wherePtr
< endPtr
) {
1535 byte
= *(wherePtr
++);
1542 } else if (nvName
== 0) {
1543 nvName
= (const char *) startPtr
;
1545 IORegistryEntry
* compareEntry
= IORegistryEntry::fromPath((const char *) nvPath
, gIODTPlane
);
1547 compareEntry
->release();
1549 if (entry
== compareEntry
) {
1550 bool appleProp
= IsApplePropertyName(nvName
);
1551 if (!appleProp
|| !resultName
) {
1552 resultName
= nvName
;
1553 resultValue
= startPtr
;
1554 resultValueLen
= wherePtr
- startPtr
- 1;
1563 startPtr
= wherePtr
;
1566 *name
= OSSymbol::withCString(resultName
);
1567 *value
= unescapeBytesToData(resultValue
, resultValueLen
);
1568 if ((*name
!= 0) && (*value
!= 0)) {
1569 err
= kIOReturnSuccess
;
1571 err
= kIOReturnNoMemory
;
1578 IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry
*entry
,
1579 const OSSymbol
*propName
,
1582 OSData
*oldData
, *escapedData
;
1584 const UInt8
*startPtr
;
1585 const UInt8
*propStart
;
1586 const UInt8
*endPtr
;
1587 const UInt8
*wherePtr
;
1588 const UInt8
*nvPath
= 0;
1589 const char *nvName
= 0;
1594 bool settingAppleProp
;
1597 return kIOReturnNoResources
;
1600 settingAppleProp
= IsApplePropertyName(propName
->getCStringNoCopy());
1602 // copy over existing properties for other entries
1604 IOLockLock(_ofLock
);
1606 oldData
= OSDynamicCast(OSData
, _ofDict
->getObject(_registryPropertiesKey
));
1608 startPtr
= (const UInt8
*) oldData
->getBytesNoCopy();
1609 endPtr
= startPtr
+ oldData
->getLength();
1611 propStart
= startPtr
;
1612 wherePtr
= startPtr
;
1613 while (wherePtr
< endPtr
) {
1614 byte
= *(wherePtr
++);
1620 } else if (nvName
== 0) {
1621 nvName
= (const char *) startPtr
;
1623 IORegistryEntry
* compareEntry
= IORegistryEntry::fromPath((const char *) nvPath
, gIODTPlane
);
1625 compareEntry
->release();
1627 if (entry
== compareEntry
) {
1628 if ((settingAppleProp
&& propName
->isEqualTo(nvName
))
1629 || (!settingAppleProp
&& !IsApplePropertyName(nvName
))) {
1630 // delete old property (nvPath -> wherePtr)
1631 data
= OSData::withBytes(propStart
, nvPath
- propStart
);
1633 ok
&= data
->appendBytes(wherePtr
, endPtr
- wherePtr
);
1642 startPtr
= wherePtr
;
1646 // make the new property
1650 data
= OSData::withData(oldData
);
1652 data
= OSData::withCapacity(16);
1659 if (ok
&& value
&& value
->getLength()) {
1661 // get entries in path
1662 OSArray
*array
= OSArray::withCapacity(5);
1668 array
->setObject(entry
);
1669 } while ((entry
= entry
->getParentEntry(gIODTPlane
)));
1672 for (int i
= array
->getCount() - 3;
1673 (entry
= (IORegistryEntry
*) array
->getObject(i
));
1675 name
= entry
->getName(gIODTPlane
);
1676 comp
= entry
->getLocation(gIODTPlane
);
1678 ok
&= data
->appendBytes("/@", 2);
1683 ok
&= data
->appendByte('/', 1);
1686 ok
&= data
->appendBytes(comp
, strlen(comp
));
1688 ok
&= data
->appendByte(0, 1);
1692 ok
&= data
->appendBytes(propName
->getCStringNoCopy(), propName
->getLength() + 1);
1694 // append escaped data
1695 escapedData
= escapeDataToData(value
);
1696 ok
&= (escapedData
!= 0);
1698 ok
&= data
->appendBytes(escapedData
);
1705 ok
= _ofDict
->setObject(_registryPropertiesKey
, data
);
1713 if (syncVariables() != kIOReturnSuccess
) {
1715 _ofDict
->setObject(_registryPropertiesKey
, oldData
);
1717 _ofDict
->removeObject(_registryPropertiesKey
);
1719 (void) syncVariables();
1728 IOLockUnlock(_ofLock
);
1730 return ok
? kIOReturnSuccess
: kIOReturnNoMemory
;
1734 IODTNVRAM::safeToSync(void)
1740 // delta interval went by
1741 clock_get_uptime(&delta
);
1743 // Figure it in seconds.
1744 absolutetime_to_nanoseconds(delta
, &delta_ns
);
1745 delta_secs
= (SInt32
)(delta_ns
/ NSEC_PER_SEC
);
1747 if ((delta_secs
> (_lastDeviceSync
+ MIN_SYNC_NOW_INTERVAL
)) || _freshInterval
) {
1748 _lastDeviceSync
= delta_secs
;
1749 _freshInterval
= FALSE
;