#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IOUserClient.h>
#include <IOKit/IOKitKeys.h>
+#include <IOKit/IOKitKeysPrivate.h>
#include <kern/debug.h>
#include <pexpert/pexpert.h>
+#if CONFIG_MACF
+extern "C" {
+#include <security/mac.h>
+#include <security/mac_framework.h>
+};
+#endif /* MAC */
+
#define super IOService
#define kIONVRAMPrivilege kIOClientPrivilegeAdministrator
// <rdar://problem/9529235> race condition possible between
// IODTNVRAM and IONVRAMController (restore loses boot-args)
initProxyData();
-
+
return true;
}
data = OSDynamicCast(OSData, prop);
if (data != 0) {
bytes = data->getBytesNoCopy();
- if (bytes != 0) {
+ if ((bytes != 0) && (data->getLength() <= kIODTNVRAMImageSize)) {
bcopy(bytes, _nvramImage, data->getLength());
initNVRAMImage();
_isProxied = true;
if (!_isProxied) {
_nvramController->read(0, _nvramImage, kIODTNVRAMImageSize);
initNVRAMImage();
+ } else {
+ IOLockLock(_ofLock);
+ (void) syncVariables();
+ IOLockUnlock(_ofLock);
}
}
// Look through the partitions to find the OF, MacOS partitions.
while (currentOffset < kIODTNVRAMImageSize) {
currentLength = ((UInt16 *)(_nvramImage + currentOffset))[1] * 16;
-
+
+ if (currentLength < 16) break;
partitionOffset = currentOffset + 16;
partitionLength = currentLength - 16;
+ if ((partitionOffset + partitionLength) > kIODTNVRAMImageSize) break;
if (strncmp((const char *)_nvramImage + currentOffset + 4,
kIODTNVRAMOFPartitionName, 12) == 0) {
// Set the partition checksum.
_nvramImage[freePartitionOffset + 1] =
calculatePartitionChecksum(_nvramImage + freePartitionOffset);
-
- // Set the nvram image as dirty.
- _nvramImageDirty = true;
+
+ if (_nvramController != 0) {
+ _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
+ }
}
} else {
_piImage = _nvramImage + _piPartitionOffset;
initOFVariables();
}
-void IODTNVRAM::sync(void)
+void IODTNVRAM::syncInternal(bool rateLimit)
{
- if (!_nvramImageDirty && !_ofImageDirty) return;
-
- // Don't try to sync OF Variables if the system has already paniced.
- if (!_systemPaniced) syncOFVariables();
-
// Don't try to perform controller operations if none has been registered.
if (_nvramController == 0) return;
+
+ // Rate limit requests to sync. Drivers that need this rate limiting will
+ // shadow the data and only write to flash when they get a sync call
+ if (rateLimit && !safeToSync()) return;
- _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
_nvramController->sync();
-
- _nvramImageDirty = false;
+}
+
+void IODTNVRAM::sync(void)
+{
+ syncInternal(false);
}
bool IODTNVRAM::serializeProperties(OSSerialize *s) const
variablePerm = getOFVariablePerm(key);
if ((hasPrivilege || (variablePerm != kOFVariablePermRootOnly)) &&
- ( ! (variablePerm == kOFVariablePermKernelOnly && current_task() != kernel_task) )) {}
- else dict->removeObject(key);
+ ( ! (variablePerm == kOFVariablePermKernelOnly && current_task() != kernel_task) )
+#if CONFIG_MACF
+ && (current_task() == kernel_task || mac_iokit_check_nvram_get(kauth_cred_get(), key->getCStringNoCopy()) == 0)
+#endif
+ ) { }
+ else {
+ dict->removeObject(key);
+ iter->reset();
+ }
}
}
}
if (variablePerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return 0;
+#if CONFIG_MACF
+ if (current_task() != kernel_task &&
+ mac_iokit_check_nvram_get(kauth_cred_get(), aKey->getCStringNoCopy()) != 0)
+ return 0;
+#endif
+
IOLockLock(_ofLock);
theObject = _ofDict->getObject(aKey);
if (theObject) theObject->retain();
bool result;
UInt32 propType, propPerm;
OSString *tmpString;
- OSObject *propObject = 0;
-
+ OSObject *propObject = 0, *oldObject;
+
if (_ofDict == 0) return false;
-
+
// Verify permissions.
propPerm = getOFVariablePerm(aKey);
- result = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege);
- if (result != kIOReturnSuccess) {
+ if (IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) != kIOReturnSuccess) {
if (propPerm != kOFVariablePermUserWrite) return false;
}
if (propPerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return 0;
// Don't allow change of 'aapl,panic-info'.
if (aKey->isEqualTo(kIODTNVRAMPanicInfoKey)) return false;
+
+#if CONFIG_MACF
+ if (current_task() != kernel_task &&
+ mac_iokit_check_nvram_set(kauth_cred_get(), aKey->getCStringNoCopy(), anObject) != 0)
+ return false;
+#endif
// Make sure the object is of the correct type.
propType = getOFVariableType(aKey);
case kOFVariableTypeBoolean :
propObject = OSDynamicCast(OSBoolean, anObject);
break;
-
+
case kOFVariableTypeNumber :
propObject = OSDynamicCast(OSNumber, anObject);
break;
-
+
case kOFVariableTypeString :
propObject = OSDynamicCast(OSString, anObject);
break;
-
+
case kOFVariableTypeData :
propObject = OSDynamicCast(OSData, anObject);
if (propObject == 0) {
}
break;
}
-
+
if (propObject == 0) return false;
IOLockLock(_ofLock);
+
+ oldObject = _ofDict->getObject(aKey);
+ if (oldObject) {
+ oldObject->retain();
+ }
result = _ofDict->setObject(aKey, propObject);
- IOLockUnlock(_ofLock);
-
+
if (result) {
- _ofImageDirty = true;
+ if (syncVariables() != kIOReturnSuccess) {
+ if (oldObject) {
+ _ofDict->setObject(aKey, oldObject);
+ }
+ else {
+ _ofDict->removeObject(aKey);
+ }
+ (void) syncVariables();
+ result = false;
+ }
}
-
+
+ if (oldObject) {
+ oldObject->release();
+ }
+
+ IOLockUnlock(_ofLock);
+
return result;
}
// Don't allow change of 'aapl,panic-info'.
if (aKey->isEqualTo(kIODTNVRAMPanicInfoKey)) return;
+#if CONFIG_MACF
+ if (current_task() != kernel_task &&
+ mac_iokit_check_nvram_delete(kauth_cred_get(), aKey->getCStringNoCopy()) != 0)
+ return;
+#endif
+
// If the object exists, remove it from the dictionary.
IOLockLock(_ofLock);
result = _ofDict->getObject(aKey) != 0;
if (result) {
_ofDict->removeObject(aKey);
- _ofImageDirty = true;
}
+
+ if (result) {
+ (void) syncVariables();
+ }
+
IOLockUnlock(_ofLock);
}
} else {
result = false;
}
- } else if(key->isEqualTo(kIONVRAMSyncNowPropertyKey)) {
+ } else if(key->isEqualTo(kIONVRAMSyncNowPropertyKey) || key->isEqualTo(kIONVRAMForceSyncNowPropertyKey)) {
tmpStr = OSDynamicCast(OSString, object);
if (tmpStr != 0) {
- result = true; // We are not going to gaurantee sync, this is best effort
+ result = true;
- if(safeToSync())
- sync();
+ // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
+
+ syncInternal(key->isEqualTo(kIONVRAMSyncNowPropertyKey));
} else {
result = false;
IOByteCount length)
{
OSNumber *partitionOffsetNumber, *partitionLengthNumber;
- UInt32 partitionOffset, partitionLength;
+ UInt32 partitionOffset, partitionLength, end;
partitionOffsetNumber =
(OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
partitionOffset = partitionOffsetNumber->unsigned32BitValue();
partitionLength = partitionLengthNumber->unsigned32BitValue();
- if ((buffer == 0) || (length == 0) ||
- (offset + length > partitionLength))
+ if (os_add_overflow(offset, length, &end)) return kIOReturnBadArgument;
+ if ((buffer == 0) || (length == 0) || (end > partitionLength))
return kIOReturnBadArgument;
bcopy(_nvramImage + partitionOffset + offset, buffer, length);
IOByteCount length)
{
OSNumber *partitionOffsetNumber, *partitionLengthNumber;
- UInt32 partitionOffset, partitionLength;
+ UInt32 partitionOffset, partitionLength, end;
partitionOffsetNumber =
(OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
partitionOffset = partitionOffsetNumber->unsigned32BitValue();
partitionLength = partitionLengthNumber->unsigned32BitValue();
- if ((buffer == 0) || (length == 0) ||
- (offset + length > partitionLength))
+ if (os_add_overflow(offset, length, &end)) return kIOReturnBadArgument;
+ if ((buffer == 0) || (length == 0) || (end > partitionLength))
return kIOReturnBadArgument;
bcopy(buffer, _nvramImage + partitionOffset + offset, length);
- _nvramImageDirty = true;
+ if (_nvramController != 0) {
+ _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
+ }
return kIOReturnSuccess;
}
// Save the Panic Info length.
*(UInt32 *)_piImage = length;
- _nvramImageDirty = true;
+ if (_nvramController != 0) {
+ _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
+ }
/*
* This prevents OF variables from being committed if the system has panicked
*/
}
}
- // Create the 'aapl,panic-info' property if needed.
if (_piImage != 0) {
propDataLength = *(UInt32 *)_piImage;
if ((propDataLength != 0) && (propDataLength <= (_piPartitionSize - 4))) {
// Clear the length from _piImage and mark dirty.
*(UInt32 *)_piImage = 0;
- _nvramImageDirty = true;
+ if (_nvramController != 0) {
+ _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
+ }
}
}
}
IOReturn IODTNVRAM::syncOFVariables(void)
+{
+ return kIOReturnUnsupported;
+}
+
+IOReturn IODTNVRAM::syncVariables(void)
{
bool ok;
UInt32 length, maxLength;
const OSSymbol *tmpSymbol;
OSObject *tmpObject;
OSCollectionIterator *iter;
-
- if ((_ofImage == 0) || (_ofDict == 0)) return kIOReturnNotReady;
-
- if (!_ofImageDirty) return kIOReturnSuccess;
-
+
+ IOLockAssert(_ofLock, kIOLockAssertOwned);
+
+ if ((_ofImage == 0) || (_ofDict == 0) || _systemPaniced) return kIOReturnNotReady;
+
buffer = tmpBuffer = IONew(UInt8, _ofPartitionSize);
if (buffer == 0) return kIOReturnNoMemory;
bzero(buffer, _ofPartitionSize);
-
+
ok = true;
maxLength = _ofPartitionSize;
- IOLockLock(_ofLock);
iter = OSCollectionIterator::withCollection(_ofDict);
if (iter == 0) ok = false;
-
+
while (ok) {
tmpSymbol = OSDynamicCast(OSSymbol, iter->getNextObject());
if (tmpSymbol == 0) break;
-
+
// Don't save 'aapl,panic-info'.
if (tmpSymbol->isEqualTo(kIODTNVRAMPanicInfoKey)) continue;
-
+
tmpObject = _ofDict->getObject(tmpSymbol);
-
+
length = maxLength;
ok = convertObjectToProp(tmpBuffer, &length, tmpSymbol, tmpObject);
if (ok) {
}
}
iter->release();
- IOLockUnlock(_ofLock);
-
+
if (ok) {
bcopy(buffer, _ofImage, _ofPartitionSize);
}
-
+
IODelete(buffer, UInt8, _ofPartitionSize);
-
+
if (!ok) return kIOReturnBadArgument;
-
- _ofImageDirty = false;
- _nvramImageDirty = true;
-
- return kIOReturnSuccess;
+
+ if (_nvramController != 0) {
+ return _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
+ }
+
+ return kIOReturnNotReady;
}
struct OFVariable {
kOWVariableOffsetString = 17
};
+static const
OFVariable gOFVariables[] = {
{"little-endian?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 0},
{"real-mode?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 1},
{"security-password", kOFVariableTypeData, kOFVariablePermRootOnly, -1},
{"boot-image", kOFVariableTypeData, kOFVariablePermUserWrite, -1},
{"com.apple.System.fp-state", kOFVariableTypeData, kOFVariablePermKernelOnly, -1},
+#if CONFIG_EMBEDDED
+ {"backlight-level", kOFVariableTypeData, kOFVariablePermUserWrite, -1},
+ {"com.apple.System.sep.art", kOFVariableTypeData, kOFVariablePermKernelOnly, -1},
+ {"com.apple.System.boot-nonce", kOFVariableTypeString, kOFVariablePermKernelOnly, -1},
+ {"darkboot", kOFVariableTypeBoolean, kOFVariablePermUserWrite, -1},
+ {"acc-mb-ld-lifetime", kOFVariableTypeNumber, kOFVariablePermKernelOnly, -1},
+ {"acc-cm-override-charger-count", kOFVariableTypeNumber, kOFVariablePermKernelOnly, -1},
+ {"acc-cm-override-count", kOFVariableTypeNumber, kOFVariablePermKernelOnly, -1},
+ {"enter-tdm-mode", kOFVariableTypeBoolean, kOFVariablePermUserWrite, -1},
+#endif
{0, kOFVariableTypeData, kOFVariablePermUserRead, -1}
};
UInt32 IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const
{
- OFVariable *ofVar;
+ const OFVariable *ofVar;
ofVar = gOFVariables;
while (1) {
UInt32 IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
{
- OFVariable *ofVar;
+ const OFVariable *ofVar;
ofVar = gOFVariables;
while (1) {
bool IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol,
UInt32 *propType, UInt32 *propOffset)
{
- OFVariable *ofVar;
+ const OFVariable *ofVar;
ofVar = gOFVariables;
while (1) {
const OSSymbol *propSymbol, OSObject *propObject)
{
const UInt8 *propName;
- UInt32 propNameLength, propDataLength;
+ UInt32 propNameLength, propDataLength, remaining;
UInt32 propType, tmpValue;
OSBoolean *tmpBoolean = 0;
OSNumber *tmpNumber = 0;
// Copy the property name equal sign.
buffer += snprintf((char *)buffer, *length, "%s=", propName);
-
+ remaining = *length - propNameLength - 1;
+
switch (propType) {
case kOFVariableTypeBoolean :
if (tmpBoolean->getValue()) {
- strlcpy((char *)buffer, "true", *length - propNameLength);
+ strlcpy((char *)buffer, "true", remaining);
} else {
- strlcpy((char *)buffer, "false", *length - propNameLength);
+ strlcpy((char *)buffer, "false", remaining);
}
break;
case kOFVariableTypeNumber :
tmpValue = tmpNumber->unsigned32BitValue();
if (tmpValue == 0xFFFFFFFF) {
- strlcpy((char *)buffer, "-1", *length - propNameLength);
+ strlcpy((char *)buffer, "-1", remaining);
} else if (tmpValue < 1000) {
- snprintf((char *)buffer, *length - propNameLength, "%d", (uint32_t)tmpValue);
+ snprintf((char *)buffer, remaining, "%d", (uint32_t)tmpValue);
} else {
- snprintf((char *)buffer, *length - propNameLength, "0x%x", (uint32_t)tmpValue);
+ snprintf((char *)buffer, remaining, "0x%x", (uint32_t)tmpValue);
}
break;
case kOFVariableTypeString :
- strlcpy((char *)buffer, tmpString->getCStringNoCopy(), *length - propNameLength);
+ strlcpy((char *)buffer, tmpString->getCStringNoCopy(), remaining);
break;
case kOFVariableTypeData :
const OSSymbol *propName,
OSData *value)
{
- OSData *oldData;
+ OSData *oldData, *escapedData;
OSData *data = 0;
const UInt8 *startPtr;
const UInt8 *propStart;
oldData = OSDynamicCast(OSData, _ofDict->getObject(_registryPropertiesKey));
if (oldData) {
+
startPtr = (const UInt8 *) oldData->getBytesNoCopy();
endPtr = startPtr + oldData->getLength();
-
+
propStart = startPtr;
wherePtr = startPtr;
while (wherePtr < endPtr) {
nvPath = 0;
nvName = 0;
}
-
+
startPtr = wherePtr;
}
}
// append prop name
ok &= data->appendBytes(propName->getCStringNoCopy(), propName->getLength() + 1);
-
+
// append escaped data
- oldData = escapeDataToData(value);
- ok &= (oldData != 0);
- if (ok) ok &= data->appendBytes(oldData);
+ escapedData = escapeDataToData(value);
+ ok &= (escapedData != 0);
+ if (ok) ok &= data->appendBytes(escapedData);
} while (false);
+ oldData->retain();
if (ok) {
ok = _ofDict->setObject(_registryPropertiesKey, data);
- if (ok) _ofImageDirty = true;
}
- IOLockUnlock(_ofLock);
if (data) data->release();
+ if (ok) {
+ if (syncVariables() != kIOReturnSuccess) {
+ if (oldData) {
+ _ofDict->setObject(_registryPropertiesKey, oldData);
+ }
+ else {
+ _ofDict->removeObject(_registryPropertiesKey);
+ }
+ (void) syncVariables();
+ ok = false;
+ }
+ }
+
+ if (oldData) {
+ oldData->release();
+ }
+
+ IOLockUnlock(_ofLock);
+
return ok ? kIOReturnSuccess : kIOReturnNoMemory;
}