+
+ _commonDict = newDict;
+
+ DEBUG_INFO("common dictionary flushed\n");
+ }
+
+ special = true;
+ }
+
+exit:
+ if (error) {
+ *error = err;
+ }
+
+ return special;
+}
+
+OSSharedPtr<OSObject>
+IODTNVRAM::copyProperty(const OSSymbol *aKey) const
+{
+ IOReturn result;
+ const char *variableName;
+ uuid_t varGuid;
+ OSDictionary *dict;
+ OSSharedPtr<OSObject> theObject = nullptr;
+
+ if (aKey->isEqualTo(kIOBSDNameKey) ||
+ aKey->isEqualTo(kIOBSDNamesKey) ||
+ aKey->isEqualTo(kIOBSDMajorKey) ||
+ aKey->isEqualTo(kIOBSDMinorKey) ||
+ aKey->isEqualTo(kIOBSDUnitKey)) {
+ // These will never match.
+ // Check here and exit to avoid logging spam
+ return nullptr;
+ }
+ DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
+
+ parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
+
+ result = chooseDictionary(kIONVRAMOperationRead, &varGuid, variableName, &dict);
+ if (result != kIOReturnSuccess) {
+ goto exit;
+ }
+
+ if (!verifyPermission(kIONVRAMOperationRead, &varGuid, variableName)) {
+ DEBUG_INFO("Not privileged\n");
+ goto exit;
+ }
+
+ NVRAMLOCK();
+ theObject.reset(dict->getObject(variableName), OSRetain);
+ NVRAMUNLOCK();
+
+ if (theObject != nullptr) {
+ DEBUG_INFO("found data\n");
+ }
+
+exit:
+ return theObject;
+}
+
+OSSharedPtr<OSObject>
+IODTNVRAM::copyProperty(const char *aKey) const
+{
+ OSSharedPtr<const OSSymbol> keySymbol;
+ OSSharedPtr<OSObject> theObject;
+
+ keySymbol = OSSymbol::withCString(aKey);
+ if (keySymbol != nullptr) {
+ theObject = copyProperty(keySymbol.get());
+ }
+
+ return theObject;
+}
+
+OSObject *
+IODTNVRAM::getProperty(const OSSymbol *aKey) const
+{
+ // The shared pointer gets released at the end of the function,
+ // and returns a view into theObject.
+ OSSharedPtr<OSObject> theObject = copyProperty(aKey);
+
+ return theObject.get();
+}
+
+OSObject *
+IODTNVRAM::getProperty(const char *aKey) const
+{
+ // The shared pointer gets released at the end of the function,
+ // and returns a view into theObject.
+ OSSharedPtr<OSObject> theObject = copyProperty(aKey);
+
+ return theObject.get();
+}
+
+IOReturn
+IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
+{
+ IOReturn result = kIOReturnSuccess;
+ bool remove = false;
+ OSString *tmpString = nullptr;
+ OSSharedPtr<OSObject> propObject, oldObject;
+ OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
+ const char *variableName;
+ uuid_t varGuid;
+ OSDictionary *dict;
+ bool deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
+ bool ok;
+ size_t propDataSize = 0;
+
+ DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
+
+ parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
+ deletePropertyKey = strncmp(variableName, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
+ syncNowPropertyKey = strncmp(variableName, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
+ forceSyncNowPropertyKey = strncmp(variableName, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
+
+ if (deletePropertyKey) {
+ tmpString = OSDynamicCast(OSString, anObject);
+ if (tmpString != nullptr) {
+ DEBUG_INFO("kIONVRAMDeletePropertyKey found\n");
+ OSSharedPtr<const OSSymbol> sharedKey = OSSymbol::withString(tmpString);
+ removeProperty(sharedKey.get());
+ } else {
+ DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
+ result = kIOReturnError;
+ }
+ goto exit;
+ } else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
+ tmpString = OSDynamicCast(OSString, anObject);
+ DEBUG_INFO("NVRAM sync key %s found\n", aKey->getCStringNoCopy());
+ if (tmpString != nullptr) {
+ // We still want to throttle NVRAM commit rate for SyncNow. ForceSyncNow is provided as a really big hammer.
+ syncInternal(syncNowPropertyKey);
+ } else {
+ DEBUG_INFO("%s value needs to be an OSString\n", variableName);
+ result = kIOReturnError;
+ }
+ goto exit;
+ }
+
+ result = chooseDictionary(kIONVRAMOperationWrite, &varGuid, variableName, &dict);
+ if (result != kIOReturnSuccess) {
+ goto exit;
+ }
+
+ if (!verifyPermission(kIONVRAMOperationWrite, &varGuid, variableName)) {
+ DEBUG_INFO("Not privileged\n");
+ result = kIOReturnNotPrivileged;
+ goto exit;
+ }
+
+ // Make sure the object is of the correct type.
+ switch (getVariableType(variableName)) {
+ case kOFVariableTypeBoolean:
+ propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
+ break;
+
+ case kOFVariableTypeNumber:
+ propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
+ break;
+
+ case kOFVariableTypeString:
+ propObject = OSDynamicPtrCast<OSString>(sharedObject);
+ if (propObject != nullptr) {
+ propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
+
+ if (aKey->isEqualTo(kIONVRAMBootArgsKey) && (propDataSize >= BOOT_LINE_LENGTH)) {
+ DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
+ result = kIOReturnNoSpace;
+ goto exit;
+ }
+ }
+ break;
+
+ case kOFVariableTypeData:
+ propObject = OSDynamicPtrCast<OSData>(sharedObject);
+ if (propObject == nullptr) {
+ tmpString = OSDynamicCast(OSString, sharedObject.get());
+ if (tmpString != nullptr) {
+ propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
+ tmpString->getLength());
+ }
+ }
+
+ if (propObject != nullptr) {
+ propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
+ }
+
+#if defined(XNU_TARGET_OS_OSX)
+ if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
+ remove = true;
+ }
+#endif /* defined(XNU_TARGET_OS_OSX) */
+ break;
+ default:
+ break;
+ }
+
+ if (propObject == nullptr) {
+ DEBUG_INFO("No property object\n");
+ result = kIOReturnBadArgument;
+ goto exit;
+ }
+
+ if (!verifyWriteSizeLimit(&varGuid, variableName, propDataSize)) {
+ DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, variableName);
+ result = kIOReturnNoSpace;
+ goto exit;
+ }
+
+ NVRAMLOCK();
+ ok = handleSpecialVariables(variableName, &varGuid, propObject.get(), &result);
+ NVRAMUNLOCK();
+
+ if (ok) {
+ serializeVariables();
+ goto exit;
+ }
+
+ NVRAMLOCK();
+ oldObject.reset(dict->getObject(variableName), OSRetain);
+ if (remove == false) {
+ DEBUG_INFO("Adding object\n");
+ if (!dict->setObject(variableName, propObject.get())) {
+ result = kIOReturnBadArgument;
+ }
+ } else {
+ DEBUG_INFO("Removing object\n");
+ // Check for existence so we can decide whether we need to sync variables
+ if (oldObject) {
+ result = removePropertyInternal(aKey);
+ } else {
+ result = kIOReturnNotFound;
+ }
+ }
+ NVRAMUNLOCK();
+
+ if (result == kIOReturnSuccess) {
+ result = serializeVariables();
+ if (result != kIOReturnSuccess) {
+ DEBUG_ERROR("serializeVariables failed, result=0x%08x\n", result);
+
+ NVRAMLOCK();
+ if (oldObject) {
+ dict->setObject(variableName, oldObject.get());
+ } else {
+ dict->removeObject(variableName);
+ }
+ NVRAMUNLOCK();
+
+ (void) serializeVariables();
+ result = kIOReturnNoMemory;
+ }
+ }
+
+ if (oldObject) {
+ oldObject.reset();
+ }
+ if (tmpString) {
+ propObject.reset();
+ }
+
+exit:
+ DEBUG_INFO("result=0x%08x\n", result);
+
+ return result;
+}
+
+bool
+IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
+{
+ return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
+}
+
+void
+IODTNVRAM::removeProperty(const OSSymbol *aKey)
+{
+ IOReturn ret;
+
+ NVRAMLOCK();
+ ret = removePropertyInternal(aKey);
+ NVRAMUNLOCK();
+
+ if (ret == kIOReturnSuccess) {
+ serializeVariables();
+ } else {
+ DEBUG_INFO("removePropertyInternal failed, ret=0x%08x\n", ret);
+ }
+}
+
+IOReturn
+IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
+{
+ IOReturn result;
+ const char *variableName;
+ uuid_t varGuid;
+ OSDictionary *dict;
+
+ DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
+
+ NVRAMLOCKASSERT();
+
+ parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
+
+ result = chooseDictionary(kIONVRAMOperationDelete, &varGuid, variableName, &dict);
+ if (result != kIOReturnSuccess) {
+ goto exit;
+ }
+
+ if (!verifyPermission(kIONVRAMOperationDelete, &varGuid, variableName)) {
+ DEBUG_INFO("Not priveleged\n");
+ result = kIOReturnNotPrivileged;
+ goto exit;
+ }
+
+ // If the object exists, remove it from the dictionary.
+ if (dict->getObject(variableName) != nullptr) {
+ dict->removeObject(variableName);
+ }
+
+exit:
+ return result;
+}
+
+IOReturn
+IODTNVRAM::setProperties(OSObject *properties)
+{
+ IOReturn result = kIOReturnSuccess;
+ OSObject *object;
+ const OSSymbol *key;
+ OSDictionary *dict;
+ OSSharedPtr<OSCollectionIterator> iter;
+
+ dict = OSDynamicCast(OSDictionary, properties);
+ if (dict == nullptr) {
+ DEBUG_ERROR("Not a dictionary\n");
+ return kIOReturnBadArgument;
+ }
+
+ iter = OSCollectionIterator::withCollection(dict);
+ if (iter == nullptr) {
+ DEBUG_ERROR("Couldn't create iterator\n");
+ return kIOReturnBadArgument;
+ }
+
+ while (result == kIOReturnSuccess) {
+ key = OSDynamicCast(OSSymbol, iter->getNextObject());
+ if (key == nullptr) {
+ break;
+ }
+
+ object = dict->getObject(key);
+ if (object == nullptr) {
+ continue;
+ }
+
+ result = setPropertyInternal(key, object);
+ }
+
+ DEBUG_INFO("result=0x%08x\n", result);
+
+ return result;
+}
+
+IOReturn
+IODTNVRAM::readXPRAM(IOByteCount offset, UInt8 *buffer,
+ IOByteCount length)
+{
+ return kIOReturnUnsupported;
+}
+
+IOReturn
+IODTNVRAM::writeXPRAM(IOByteCount offset, UInt8 *buffer,
+ IOByteCount length)
+{
+ return kIOReturnUnsupported;
+}
+
+IOReturn
+IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
+ const OSSymbol **name,
+ OSData **value)
+{
+ IOReturn err;
+
+ err = readNVRAMPropertyType1(entry, name, value);
+
+ return err;
+}
+
+IOReturn
+IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
+ const OSSymbol *name,
+ OSData *value)
+{
+ IOReturn err;
+
+ err = writeNVRAMPropertyType1(entry, name, value);
+
+ return err;
+}
+
+OSDictionary *
+IODTNVRAM::getNVRAMPartitions(void)
+{
+ return _nvramPartitionLengths.get();
+}
+
+IOReturn
+IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
+ IOByteCount offset, UInt8 *buffer,
+ IOByteCount length)
+{
+ OSNumber *partitionOffsetNumber, *partitionLengthNumber;
+ UInt32 partitionOffset, partitionLength, end;
+
+ partitionOffsetNumber =
+ (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
+ partitionLengthNumber =
+ (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
+
+ if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
+ return kIOReturnNotFound;
+ }
+
+ partitionOffset = partitionOffsetNumber->unsigned32BitValue();
+ partitionLength = partitionLengthNumber->unsigned32BitValue();
+
+ if (os_add_overflow(offset, length, &end)) {
+ return kIOReturnBadArgument;
+ }
+ if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
+ return kIOReturnBadArgument;
+ }
+
+ bcopy(_nvramImage + partitionOffset + offset, buffer, length);
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
+ IOByteCount offset, UInt8 *buffer,
+ IOByteCount length)
+{
+ OSNumber *partitionOffsetNumber, *partitionLengthNumber;
+ UInt32 partitionOffset, partitionLength, end;
+
+ partitionOffsetNumber =
+ (OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
+ partitionLengthNumber =
+ (OSNumber *)_nvramPartitionLengths->getObject(partitionID);
+
+ if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
+ return kIOReturnNotFound;
+ }
+
+ partitionOffset = partitionOffsetNumber->unsigned32BitValue();
+ partitionLength = partitionLengthNumber->unsigned32BitValue();
+
+ if (os_add_overflow(offset, length, &end)) {
+ return kIOReturnBadArgument;
+ }
+ if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
+ return kIOReturnBadArgument;
+ }
+
+ bcopy(buffer, _nvramImage + partitionOffset + offset, length);
+
+ if (_nvramController != nullptr) {
+ _nvramController->write(0, _nvramImage, _nvramSize);
+ }
+
+ return kIOReturnSuccess;
+}
+
+IOByteCount
+IODTNVRAM::savePanicInfo(UInt8 *buffer, IOByteCount length)
+{
+ return 0;
+}
+
+// Private methods
+
+UInt8
+IODTNVRAM::calculatePartitionChecksum(UInt8 *partitionHeader)
+{
+ UInt8 cnt, isum, csum = 0;
+
+ for (cnt = 0; cnt < 0x10; cnt++) {
+ isum = csum + partitionHeader[cnt];
+ if (isum < csum) {
+ isum++;
+ }
+ csum = isum;
+ }
+
+ return csum;
+}
+
+IOReturn
+IODTNVRAM::initVariables(void)
+{
+ UInt32 cnt;
+ UInt8 *propName, *propData;
+ UInt32 propNameLength, propDataLength, regionIndex;
+ OSSharedPtr<const OSSymbol> propSymbol;
+ OSSharedPtr<OSObject> propObject;
+ NVRAMRegionInfo *currentRegion;
+ NVRAMRegionInfo variableRegions[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
+ { NVRAM_CHRP_PARTITION_NAME_SYSTEM, _systemPartitionOffset, _systemPartitionSize, _systemDict, _systemImage} };
+
+ DEBUG_INFO("...\n");
+
+ for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
+ currentRegion = &variableRegions[regionIndex];
+
+ if (currentRegion->size == 0) {
+ continue;
+ }
+
+ currentRegion->dict = OSDictionary::withCapacity(1);
+
+ DEBUG_INFO("region = %s\n", currentRegion->name);
+ cnt = 0;
+ while (cnt < currentRegion->size) {
+ // Break if there is no name.
+ if (currentRegion->image[cnt] == '\0') {
+ break;
+ }
+
+ // Find the length of the name.
+ propName = currentRegion->image + cnt;
+ for (propNameLength = 0; (cnt + propNameLength) < currentRegion->size;
+ propNameLength++) {
+ if (currentRegion->image[cnt + propNameLength] == '=') {
+ break;
+ }
+ }
+
+ // Break if the name goes past the end of the partition.
+ if ((cnt + propNameLength) >= currentRegion->size) {
+ break;
+ }
+ cnt += propNameLength + 1;
+
+ propData = currentRegion->image + cnt;
+ for (propDataLength = 0; (cnt + propDataLength) < currentRegion->size;
+ propDataLength++) {
+ if (currentRegion->image[cnt + propDataLength] == '\0') {
+ break;
+ }
+ }
+
+ // Break if the data goes past the end of the partition.
+ if ((cnt + propDataLength) >= currentRegion->size) {
+ break;
+ }
+ cnt += propDataLength + 1;
+
+ if (convertPropToObject(propName, propNameLength,
+ propData, propDataLength,
+ propSymbol, propObject)) {
+ DEBUG_INFO("adding %s, dataLength=%u\n", propSymbol.get()->getCStringNoCopy(), (unsigned int)propDataLength);
+ currentRegion->dict.get()->setObject(propSymbol.get(), propObject.get());
+ }
+ }
+ }
+
+ // Create the boot-args property if it is not in the dictionary.
+ if (_commonDict->getObject(kIONVRAMBootArgsKey) == nullptr) {
+ propObject = OSString::withCStringNoCopy("");
+ if (propObject != nullptr) {
+ _commonDict->setObject(kIONVRAMBootArgsKey, propObject.get());
+ }
+ }
+
+ DEBUG_INFO("%s _commonDict=%p _systemDict=%p\n", __FUNCTION__, _commonDict.get(), _systemDict.get());
+
+ return kIOReturnSuccess;
+}
+
+IOReturn
+IODTNVRAM::syncOFVariables(void)
+{
+ return kIOReturnUnsupported;
+}
+
+IOReturn
+IODTNVRAM::serializeVariables(void)
+{
+ IOReturn ret;
+ bool ok;
+ UInt32 length, maxLength, regionIndex;
+ UInt8 *buffer, *tmpBuffer;
+ const OSSymbol *tmpSymbol;
+ OSObject *tmpObject;
+ OSSharedPtr<OSCollectionIterator> iter;
+ OSSharedPtr<OSNumber> sizeUsed;
+ UInt32 systemUsed = 0;
+ UInt32 commonUsed = 0;
+ OSSharedPtr<OSData> nvramImage;
+ NVRAMRegionInfo *currentRegion;
+ NVRAMRegionInfo variableRegions[] = { { NVRAM_CHRP_PARTITION_NAME_COMMON, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
+ { NVRAM_CHRP_PARTITION_NAME_SYSTEM, _systemPartitionOffset, _systemPartitionSize, _systemDict, _systemImage} };
+
+ if (_systemPanicked) {
+ return kIOReturnNotReady;
+ }
+
+ if (_nvramController == nullptr) {
+ DEBUG_ERROR("No _nvramController\n");
+ return kIOReturnNotReady;
+ }
+
+ DEBUG_INFO("...\n");
+
+ NVRAMLOCK();
+
+ for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
+ currentRegion = &variableRegions[regionIndex];
+
+ if (currentRegion->size == 0) {
+ continue;
+ }
+
+ DEBUG_INFO("region = %s\n", currentRegion->name);
+ buffer = tmpBuffer = IONew(UInt8, currentRegion->size);
+ if (buffer == nullptr) {
+ return kIOReturnNoMemory;
+ }
+ bzero(buffer, currentRegion->size);
+
+ ok = true;
+ maxLength = currentRegion->size;
+
+ iter = OSCollectionIterator::withCollection(currentRegion->dict.get());
+ if (iter == nullptr) {
+ ok = false;
+ }
+
+ while (ok) {
+ tmpSymbol = OSDynamicCast(OSSymbol, iter->getNextObject());
+ if (tmpSymbol == nullptr) {
+ break;
+ }
+
+ DEBUG_INFO("adding variable %s\n", tmpSymbol->getCStringNoCopy());
+
+ tmpObject = currentRegion->dict->getObject(tmpSymbol);
+
+ length = maxLength;
+ ok = convertObjectToProp(tmpBuffer, &length, tmpSymbol, tmpObject);
+ if (ok) {
+ tmpBuffer += length;
+ maxLength -= length;
+ }
+ }
+
+ if (ok) {
+ bcopy(buffer, currentRegion->image, currentRegion->size);
+ }
+
+ IODelete(buffer, UInt8, currentRegion->size);
+
+ if ((strncmp(currentRegion->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM)) == 0) &&
+ (_systemService != nullptr)) {
+ _systemService->setProperties(_systemDict.get());
+ systemUsed = maxLength;
+ } else if ((strncmp(currentRegion->name, NVRAM_CHRP_PARTITION_NAME_COMMON, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON)) == 0) &&
+ (_commonService != nullptr)) {
+ _commonService->setProperties(_commonDict.get());
+ commonUsed = maxLength;
+ }
+
+ if (!ok) {
+ return kIOReturnBadArgument;
+ }
+ }
+
+ nvramImage = OSData::withBytes(_nvramImage, _nvramSize);
+
+ NVRAMUNLOCK();
+
+ DEBUG_INFO("ok=%d\n", ok);
+
+ CONTROLLERLOCK();
+
+ if (_systemService) {
+ sizeUsed = OSNumber::withNumber(systemUsed, 32);
+ _nvramController->setProperty("SystemUsed", sizeUsed.get());
+ sizeUsed.reset();
+ }
+
+ if (_commonService) {
+ sizeUsed = OSNumber::withNumber(commonUsed, 32);
+ _nvramController->setProperty("CommonUsed", sizeUsed.get());
+ sizeUsed.reset();
+ }
+
+ ret = _nvramController->write(0, (uint8_t *)nvramImage->getBytesNoCopy(), nvramImage->getLength());
+
+ CONTROLLERUNLOCK();
+
+ return ret;
+}
+
+UInt32
+IODTNVRAM::getOFVariableType(const char *propName) const
+{
+ return 0;
+}
+
+UInt32
+IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const
+{
+ return 0;
+}
+
+
+UInt32
+IODTNVRAM::getOFVariablePerm(const char *propName) const
+{
+ return 0;
+}
+
+UInt32
+IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
+{
+ return 0;
+}
+
+bool
+IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol,
+ UInt32 *propType, UInt32 *propOffset)
+{
+ /* UNSUPPORTED */
+ return false;
+}
+bool
+IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
+ UInt8 *propData, UInt32 propDataLength,
+ const OSSymbol **propSymbol,
+ OSObject **propObject)
+{
+ OSSharedPtr<const OSSymbol> tmpSymbol;
+ OSSharedPtr<OSNumber> tmpNumber;
+ OSSharedPtr<OSString> tmpString;
+ OSSharedPtr<OSObject> tmpObject = nullptr;
+
+ propName[propNameLength] = '\0';
+ tmpSymbol = OSSymbol::withCString((const char *)propName);
+ propName[propNameLength] = '=';
+ if (tmpSymbol == nullptr) {
+ return false;
+ }
+
+ switch (getVariableType(tmpSymbol.get())) {
+ case kOFVariableTypeBoolean:
+ if (!strncmp("true", (const char *)propData, propDataLength)) {
+ tmpObject.reset(kOSBooleanTrue, OSRetain);
+ } else if (!strncmp("false", (const char *)propData, propDataLength)) {
+ tmpObject.reset(kOSBooleanFalse, OSRetain);
+ }
+ break;
+
+ case kOFVariableTypeNumber:
+ tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32);
+ if (tmpNumber != nullptr) {
+ tmpObject = tmpNumber;
+ }
+ break;
+
+ case kOFVariableTypeString:
+ tmpString = OSString::withCString((const char *)propData);
+ if (tmpString != nullptr) {
+ tmpObject = tmpString;
+ }
+ break;
+
+ case kOFVariableTypeData:
+ tmpObject = unescapeBytesToData(propData, propDataLength);
+ break;
+
+ default:
+ break;
+ }
+
+ if (tmpObject == nullptr) {
+ tmpSymbol.reset();
+ return false;
+ }
+
+ *propSymbol = tmpSymbol.detach();
+ *propObject = tmpObject.detach();
+
+ return true;
+}
+
+bool
+IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
+ UInt8 *propData, UInt32 propDataLength,
+ OSSharedPtr<const OSSymbol>& propSymbol,
+ OSSharedPtr<OSObject>& propObject)
+{
+ const OSSymbol* propSymbolRaw = nullptr;
+ OSObject* propObjectRaw = nullptr;
+ bool result = convertPropToObject(propName, propNameLength, propData, propDataLength,
+ &propSymbolRaw, &propObjectRaw);
+ propSymbol.reset(propSymbolRaw, OSNoRetain);
+ propObject.reset(propObjectRaw, OSNoRetain);
+ return result;
+}
+
+bool
+IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length,
+ const OSSymbol *propSymbol, OSObject *propObject)
+{
+ const UInt8 *propName;
+ UInt32 propNameLength, propDataLength, remaining;
+ IONVRAMVariableType propType;
+ OSBoolean *tmpBoolean = nullptr;
+ OSNumber *tmpNumber = nullptr;
+ OSString *tmpString = nullptr;
+ OSSharedPtr<OSData> tmpData;
+
+ propName = (const UInt8 *)propSymbol->getCStringNoCopy();
+ propNameLength = propSymbol->getLength();
+ propType = getVariableType(propSymbol);
+
+ // Get the size of the data.
+ propDataLength = 0xFFFFFFFF;
+ switch (propType) {
+ case kOFVariableTypeBoolean:
+ tmpBoolean = OSDynamicCast(OSBoolean, propObject);
+ if (tmpBoolean != nullptr) {
+ propDataLength = 5;
+ }
+ break;
+
+ case kOFVariableTypeNumber:
+ tmpNumber = OSDynamicCast(OSNumber, propObject);
+ if (tmpNumber != nullptr) {
+ propDataLength = 10;
+ }
+ break;
+
+ case kOFVariableTypeString:
+ tmpString = OSDynamicCast(OSString, propObject);
+ if (tmpString != nullptr) {
+ propDataLength = tmpString->getLength();
+ }
+ break;
+
+ case kOFVariableTypeData:
+ tmpData.reset(OSDynamicCast(OSData, propObject), OSNoRetain);
+ if (tmpData != nullptr) {
+ tmpData = escapeDataToData(tmpData.detach());
+ propDataLength = tmpData->getLength();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Make sure the propertySize is known and will fit.
+ if (propDataLength == 0xFFFFFFFF) {
+ return false;
+ }
+ if ((propNameLength + propDataLength + 2) > *length) {
+ return false;
+ }
+
+ // 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", remaining);
+ } else {
+ strlcpy((char *)buffer, "false", remaining);
+ }
+ break;
+
+ case kOFVariableTypeNumber:
+ {
+ uint32_t tmpValue = tmpNumber->unsigned32BitValue();
+ if (tmpValue == 0xFFFFFFFF) {
+ strlcpy((char *)buffer, "-1", remaining);
+ } else if (tmpValue < 1000) {
+ snprintf((char *)buffer, remaining, "%d", (uint32_t)tmpValue);
+ } else {
+ snprintf((char *)buffer, remaining, "0x%x", (uint32_t)tmpValue);
+ }
+ }
+ break;
+
+ case kOFVariableTypeString:
+ strlcpy((char *)buffer, tmpString->getCStringNoCopy(), remaining);
+ break;
+
+ case kOFVariableTypeData:
+ bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength);
+ tmpData.reset();
+ break;
+
+ default:
+ break;
+ }
+
+ propDataLength = ((UInt32) strlen((const char *)buffer));
+
+ *length = propNameLength + propDataLength + 2;
+
+ return true;
+}
+
+
+UInt16
+IODTNVRAM::generateOWChecksum(UInt8 *buffer)
+{
+ UInt32 cnt, checksum = 0;
+ UInt16 *tmpBuffer = (UInt16 *)buffer;
+
+ for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
+ checksum += tmpBuffer[cnt];
+ }
+
+ return checksum % 0x0000FFFF;
+}
+
+bool
+IODTNVRAM::validateOWChecksum(UInt8 *buffer)
+{
+ UInt32 cnt, checksum, sum = 0;
+ UInt16 *tmpBuffer = (UInt16 *)buffer;
+
+ for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
+ sum += tmpBuffer[cnt];
+ }
+
+ checksum = (sum >> 16) + (sum & 0x0000FFFF);
+ if (checksum == 0x10000) {
+ checksum--;
+ }
+ checksum = (checksum ^ 0x0000FFFF) & 0x0000FFFF;
+
+ return checksum == 0;
+}
+
+void
+IODTNVRAM::updateOWBootArgs(const OSSymbol *key, OSObject *value)
+{
+ /* UNSUPPORTED */
+}
+
+bool
+IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor *hdr, UInt32 *where)
+{
+ return false;
+}
+
+IOReturn
+IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry *entry,
+ const OSSymbol **name,
+ OSData **value)
+{
+ return kIOReturnUnsupported;
+}
+
+
+IOReturn
+IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry *entry,
+ const OSSymbol *name,
+ OSData *value)
+{
+ return kIOReturnUnsupported;
+}
+
+
+OSSharedPtr<OSData>
+IODTNVRAM::unescapeBytesToData(const UInt8 *bytes, UInt32 length)
+{
+ OSSharedPtr<OSData> data;
+ UInt32 totalLength = 0;
+ UInt32 cnt, cnt2;
+ UInt8 byte;
+ bool ok;
+
+ // Calculate the actual length of the data.
+ ok = true;
+ totalLength = 0;
+ for (cnt = 0; cnt < length;) {
+ byte = bytes[cnt++];
+ if (byte == 0xFF) {
+ byte = bytes[cnt++];
+ if (byte == 0x00) {
+ ok = false;
+ break;
+ }
+ cnt2 = byte & 0x7F;
+ } else {
+ cnt2 = 1;
+ }
+ totalLength += cnt2;
+ }
+
+ if (ok) {
+ // Create an empty OSData of the correct size.
+ data = OSData::withCapacity(totalLength);
+ if (data != nullptr) {
+ for (cnt = 0; cnt < length;) {
+ byte = bytes[cnt++];
+ if (byte == 0xFF) {
+ byte = bytes[cnt++];
+ cnt2 = byte & 0x7F;
+ byte = (byte & 0x80) ? 0xFF : 0x00;
+ } else {
+ cnt2 = 1;
+ }
+ data->appendByte(byte, cnt2);
+ }
+ }
+ }
+
+ return data;
+}
+
+OSSharedPtr<OSData>
+IODTNVRAM::escapeDataToData(OSData * value)
+{
+ OSSharedPtr<OSData> result;
+ const UInt8 *startPtr;
+ const UInt8 *endPtr;
+ const UInt8 *wherePtr;
+ UInt8 byte;
+ bool ok = true;
+
+ wherePtr = (const UInt8 *) value->getBytesNoCopy();
+ endPtr = wherePtr + value->getLength();
+
+ result = OSData::withCapacity((unsigned int) (endPtr - wherePtr));
+ if (!result) {
+ return result;
+ }
+
+ while (wherePtr < endPtr) {
+ startPtr = wherePtr;
+ byte = *wherePtr++;
+ if ((byte == 0x00) || (byte == 0xFF)) {
+ for (;
+ ((wherePtr - startPtr) < 0x80) && (wherePtr < endPtr) && (byte == *wherePtr);
+ wherePtr++) {
+ }
+ ok &= result->appendByte(0xff, 1);
+ byte = (byte & 0x80) | ((UInt8)(wherePtr - startPtr));
+ }
+ ok &= result->appendByte(byte, 1);
+ }
+ ok &= result->appendByte(0, 1);
+
+ if (!ok) {
+ result.reset();
+ }
+
+ return result;
+}
+
+static bool
+IsApplePropertyName(const char * propName)
+{
+ char c;
+ while ((c = *propName++)) {
+ if ((c >= 'A') && (c <= 'Z')) {
+ break;
+ }
+ }
+
+ return c == 0;
+}
+
+IOReturn
+IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry *entry,
+ const OSSymbol **name,
+ OSData **value)
+{
+ IOReturn err = kIOReturnNoResources;
+ OSData *data;
+ const UInt8 *startPtr;
+ const UInt8 *endPtr;
+ const UInt8 *wherePtr;
+ const UInt8 *nvPath = nullptr;
+ const char *nvName = nullptr;
+ const char *resultName = nullptr;
+ const UInt8 *resultValue = nullptr;
+ UInt32 resultValueLen = 0;
+ UInt8 byte;
+
+ NVRAMLOCK();
+ data = OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get()));
+ NVRAMUNLOCK();
+
+ if (data == nullptr) {
+ return err;
+ }
+
+ startPtr = (const UInt8 *) data->getBytesNoCopy();
+ endPtr = startPtr + data->getLength();
+
+ wherePtr = startPtr;
+ while (wherePtr < endPtr) {
+ byte = *(wherePtr++);
+ if (byte) {
+ continue;
+ }
+
+ if (nvPath == nullptr) {
+ nvPath = startPtr;
+ } else if (nvName == nullptr) {
+ nvName = (const char *) startPtr;
+ } else {
+ OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
+ if (entry == compareEntry) {
+ bool appleProp = IsApplePropertyName(nvName);
+ if (!appleProp || !resultName) {
+ resultName = nvName;
+ resultValue = startPtr;
+ resultValueLen = (UInt32) (wherePtr - startPtr - 1); // OSData getLength() is 32b
+ }
+ if (!appleProp) {
+ break;
+ }
+ }
+ nvPath = nullptr;
+ nvName = nullptr;
+ }
+ startPtr = wherePtr;
+ }
+ if (resultName) {
+ *name = OSSymbol::withCString(resultName).detach();
+ *value = unescapeBytesToData(resultValue, resultValueLen).detach();
+ if ((*name != nullptr) && (*value != nullptr)) {
+ err = kIOReturnSuccess;
+ } else {
+ err = kIOReturnNoMemory;
+ }
+ }
+ return err;
+}
+
+IOReturn
+IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
+ const OSSymbol *propName,
+ OSData *value)
+{
+ OSSharedPtr<OSData> data, oldData;
+ const UInt8 *startPtr;
+ const UInt8 *propStart;
+ const UInt8 *endPtr;
+ const UInt8 *wherePtr;
+ const UInt8 *nvPath = nullptr;
+ const char *nvName = nullptr;
+ const char *comp;
+ const char *name;
+ UInt8 byte;
+ bool ok = true;
+ bool settingAppleProp;
+
+ settingAppleProp = IsApplePropertyName(propName->getCStringNoCopy());
+
+ // copy over existing properties for other entries
+
+ NVRAMLOCK();
+
+ oldData.reset(OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get())), OSRetain);
+ if (oldData) {
+ startPtr = (const UInt8 *) oldData->getBytesNoCopy();
+ endPtr = startPtr + oldData->getLength();
+
+ propStart = startPtr;
+ wherePtr = startPtr;
+ while (wherePtr < endPtr) {
+ byte = *(wherePtr++);
+ if (byte) {
+ continue;
+ }
+ if (nvPath == nullptr) {
+ nvPath = startPtr;
+ } else if (nvName == nullptr) {
+ nvName = (const char *) startPtr;
+ } else {
+ OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
+
+ if (entry == compareEntry) {
+ if ((settingAppleProp && propName->isEqualTo(nvName))
+ || (!settingAppleProp && !IsApplePropertyName(nvName))) {
+ // delete old property (nvPath -> wherePtr) source OSData len is 32b
+ data = OSData::withBytes(propStart, (UInt32)(nvPath - propStart));
+ if (data) {
+ ok &= data->appendBytes(wherePtr, (UInt32)(endPtr - wherePtr));
+ }
+ break;
+ }
+ }
+ nvPath = nullptr;
+ nvName = nullptr;
+ }
+
+ startPtr = wherePtr;
+ }
+ }
+
+ // make the new property
+
+ if (!data) {
+ if (oldData) {
+ data = OSData::withData(oldData.get());
+ } else {
+ data = OSData::withCapacity(16);
+ }
+ if (!data) {
+ ok = false;
+ }
+ }
+
+ if (ok && value && value->getLength()) {
+ do {
+ // get entries in path
+ OSSharedPtr<OSArray> array = OSArray::withCapacity(5);
+ if (!array) {
+ ok = false;
+ break;
+ }
+ do{
+ array->setObject(entry);
+ } while ((entry = entry->getParentEntry(gIODTPlane)));
+
+ // append path
+ for (int i = array->getCount() - 3;
+ (entry = (IORegistryEntry *) array->getObject(i));
+ i--) {
+ name = entry->getName(gIODTPlane);
+ comp = entry->getLocation(gIODTPlane);
+ if (comp) {
+ ok &= data->appendBytes("/@", 2);
+ } else {
+ if (!name) {
+ continue;
+ }
+ ok &= data->appendByte('/', 1);
+ comp = name;
+ }
+ ok &= data->appendBytes(comp, (unsigned int) strnlen(comp, UINT16_MAX));
+ }
+ ok &= data->appendByte(0, 1);
+ // append prop name
+ ok &= data->appendBytes(propName->getCStringNoCopy(), propName->getLength() + 1);
+
+ // append escaped data
+ OSSharedPtr<OSData> escapedData = escapeDataToData(value);
+ ok &= (escapedData != nullptr);
+ if (ok) {
+ ok &= data->appendBytes(escapedData.get());
+ }
+ } while (false);
+ }
+
+ if (ok) {
+ ok = _commonDict->setObject(_registryPropertiesKey.get(), data.get());
+ }
+
+ NVRAMUNLOCK();
+
+ if (ok) {
+ if (serializeVariables() != kIOReturnSuccess) {
+ NVRAMLOCK();
+ if (oldData) {
+ _commonDict->setObject(_registryPropertiesKey.get(), oldData.get());
+ } else {
+ _commonDict->removeObject(_registryPropertiesKey.get());
+ }
+ NVRAMUNLOCK();
+
+ (void) serializeVariables();
+ ok = false;
+ }
+ }
+
+ oldData.reset();
+
+ return ok ? kIOReturnSuccess : kIOReturnNoMemory;
+}
+
+bool
+IODTNVRAM::safeToSync(void)
+{
+ AbsoluteTime delta;
+ UInt64 delta_ns;
+ SInt32 delta_secs;
+
+ // delta interval went by
+ clock_get_uptime(&delta);
+
+ // Figure it in seconds.
+ absolutetime_to_nanoseconds(delta, &delta_ns);
+ delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
+
+ if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
+ _lastDeviceSync = delta_secs;
+ _freshInterval = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;