]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IONVRAM.cpp
xnu-4570.71.2.tar.gz
[apple/xnu.git] / iokit / Kernel / IONVRAM.cpp
index 5af96b290baff8718f1668b570c9884cc4edc6bf..94d6b75dd77d0ce7bb88afa83cf6ef4874ee2d64 100644 (file)
 #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
@@ -67,7 +75,7 @@ bool IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
   // <rdar://problem/9529235> race condition possible between
   // IODTNVRAM and IONVRAMController (restore loses boot-args)
   initProxyData();
-  
+
   return true;
 }
 
@@ -86,7 +94,7 @@ void IODTNVRAM::initProxyData(void)
       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;
@@ -109,6 +117,10 @@ void IODTNVRAM::registerNVRAMController(IONVRAMController *nvram)
   if (!_isProxied) {
     _nvramController->read(0, _nvramImage, kIODTNVRAMImageSize);
     initNVRAMImage();
+  } else {
+    IOLockLock(_ofLock);
+    (void) syncVariables();
+    IOLockUnlock(_ofLock);
   }
 }
 
@@ -129,9 +141,11 @@ void IODTNVRAM::initNVRAMImage(void)
   // 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) {
@@ -217,9 +231,10 @@ void IODTNVRAM::initNVRAMImage(void)
       // 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;
@@ -231,20 +246,21 @@ void IODTNVRAM::initNVRAMImage(void)
   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
@@ -280,8 +296,15 @@ 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();
+      }
     }
   }
 
@@ -309,6 +332,12 @@ OSObject *IODTNVRAM::copyProperty(const OSSymbol *aKey) const
   }
   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();
@@ -356,20 +385,25 @@ bool IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
   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);
@@ -377,15 +411,15 @@ bool IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
   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) {
@@ -397,17 +431,36 @@ bool IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
     }
     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;
 }
 
@@ -429,14 +482,24 @@ void IODTNVRAM::removeProperty(const OSSymbol *aKey)
   // 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);
 }
 
@@ -472,14 +535,15 @@ IOReturn IODTNVRAM::setProperties(OSObject *properties)
                } 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;
@@ -541,7 +605,7 @@ IOReturn IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
                                       IOByteCount length)
 {
   OSNumber *partitionOffsetNumber, *partitionLengthNumber;
-  UInt32   partitionOffset, partitionLength;
+  UInt32   partitionOffset, partitionLength, end;
   
   partitionOffsetNumber =
     (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
@@ -554,8 +618,8 @@ IOReturn IODTNVRAM::readNVRAMPartition(const OSSymbol *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);
@@ -568,7 +632,7 @@ IOReturn IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
                                        IOByteCount length)
 {
   OSNumber *partitionOffsetNumber, *partitionLengthNumber;
-  UInt32   partitionOffset, partitionLength;
+  UInt32   partitionOffset, partitionLength, end;
   
   partitionOffsetNumber =
     (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
@@ -581,13 +645,15 @@ IOReturn IODTNVRAM::writeNVRAMPartition(const OSSymbol *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;
 }
@@ -605,7 +671,9 @@ IOByteCount IODTNVRAM::savePanicInfo(UInt8 *buffer, IOByteCount length)
   // 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
    */
@@ -691,7 +759,6 @@ IOReturn IODTNVRAM::initOFVariables(void)
     }
   }
   
-  // Create the 'aapl,panic-info' property if needed.
   if (_piImage != 0) {
     propDataLength = *(UInt32 *)_piImage;
     if ((propDataLength != 0) && (propDataLength <= (_piPartitionSize - 4))) {
@@ -701,7 +768,9 @@ IOReturn IODTNVRAM::initOFVariables(void)
       
       // Clear the length from _piImage and mark dirty.
       *(UInt32 *)_piImage = 0;
-      _nvramImageDirty = true;
+      if (_nvramController != 0) {
+        _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize);
+      }
     }
   }
 
@@ -709,6 +778,11 @@ IOReturn IODTNVRAM::initOFVariables(void)
 }
 
 IOReturn IODTNVRAM::syncOFVariables(void)
+{
+  return kIOReturnUnsupported;
+}
+
+IOReturn IODTNVRAM::syncVariables(void)
 {
   bool                 ok;
   UInt32               length, maxLength;
@@ -716,31 +790,30 @@ IOReturn IODTNVRAM::syncOFVariables(void)
   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) {
@@ -749,20 +822,20 @@ IOReturn IODTNVRAM::syncOFVariables(void)
     }
   }
   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 {
@@ -778,6 +851,7 @@ enum {
   kOWVariableOffsetString = 17
 };
 
+static const
 OFVariable gOFVariables[] = {
   {"little-endian?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 0},
   {"real-mode?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 1},
@@ -826,12 +900,22 @@ OFVariable gOFVariables[] = {
   {"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) {
@@ -845,7 +929,7 @@ UInt32 IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const
 
 UInt32 IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
 {
-  OFVariable *ofVar;
+  const OFVariable *ofVar;
   
   ofVar = gOFVariables;
   while (1) {
@@ -860,7 +944,7 @@ UInt32 IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
 bool IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol,
                                  UInt32 *propType, UInt32 *propOffset)
 {
-  OFVariable *ofVar;
+  const OFVariable *ofVar;
   
   ofVar = gOFVariables;
   while (1) {
@@ -953,7 +1037,7 @@ bool IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length,
                                    const OSSymbol *propSymbol, OSObject *propObject)
 {
   const UInt8    *propName;
-  UInt32         propNameLength, propDataLength;
+  UInt32         propNameLength, propDataLength, remaining;
   UInt32         propType, tmpValue;
   OSBoolean      *tmpBoolean = 0;
   OSNumber       *tmpNumber = 0;
@@ -997,29 +1081,30 @@ bool IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length,
   
   // 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 :
@@ -1317,7 +1402,7 @@ IOReturn IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
                                            const OSSymbol *propName,
                                            OSData *value)
 {
-  OSData       *oldData;
+  OSData       *oldData, *escapedData;
   OSData       *data = 0;
   const UInt8  *startPtr;
   const UInt8  *propStart;
@@ -1341,9 +1426,10 @@ IOReturn IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
 
   oldData = OSDynamicCast(OSData, _ofDict->getObject(_registryPropertiesKey));
   if (oldData) {
+
     startPtr = (const UInt8 *) oldData->getBytesNoCopy();
     endPtr = startPtr + oldData->getLength();
-    
+
     propStart = startPtr;
     wherePtr = startPtr;
     while (wherePtr < endPtr) {
@@ -1371,7 +1457,7 @@ IOReturn IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
         nvPath = 0;
         nvName = 0;
       }
-        
+
       startPtr = wherePtr;
     }
   }
@@ -1417,22 +1503,40 @@ IOReturn IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
 
     // 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;
 }