Loading...
--- xnu/xnu-12377.101.15/iokit/Kernel/IONVRAMCHRPHandler.cpp
+++ /dev/null
@@ -1,1282 +0,0 @@
-/*
- * Copyright (c) 2021-2022 Apple Inc. All rights reserved.
- *
- * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. The rights granted to you under the License
- * may not be used to create, or enable the creation or redistribution of,
- * unlawful or unlicensed copies of an Apple operating system, or to
- * circumvent, violate, or enable the circumvention or violation of, any
- * terms of an Apple operating system software license agreement.
- *
- * Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
- */
-
-#include <libkern/c++/OSBoundedPtr.h>
-
-#define NVRAM_CHRP_APPLE_HEADER_NAME_V1 "nvram"
-#define NVRAM_CHRP_APPLE_HEADER_NAME_V2 "2nvram"
-
-#define NVRAM_CHRP_PARTITION_NAME_COMMON_V1 "common"
-#define NVRAM_CHRP_PARTITION_NAME_SYSTEM_V1 "system"
-#define NVRAM_CHRP_PARTITION_NAME_COMMON_V2 "2common"
-#define NVRAM_CHRP_PARTITION_NAME_SYSTEM_V2 "2system"
-
-#define NVRAM_CHRP_LENGTH_BLOCK_SIZE 0x10 // CHRP length field is in 16 byte blocks
-
-typedef struct chrp_nvram_header { //16 bytes
- uint8_t sig;
- uint8_t cksum; // checksum on sig, len, and name
- uint16_t len; // total length of the partition in 16 byte blocks starting with the signature
- // and ending with the last byte of data area, ie len includes its own header size
- char name[12];
- uint8_t data[0];
-} chrp_nvram_header_t;
-
-typedef struct apple_nvram_header { // 16 + 16 bytes
- struct chrp_nvram_header chrp;
- uint32_t adler;
- uint32_t generation;
- uint8_t padding[8];
-} apple_nvram_header_t;
-
-typedef struct {
- NVRAMPartitionType type;
- uint32_t offset;
- uint32_t size;
-} NVRAMRegionInfo;
-
-class IONVRAMCHRPHandler : public IODTNVRAMFormatHandler, IOTypedOperatorsMixin<IONVRAMCHRPHandler>
-{
-private:
- bool _newData;
- bool _reload;
- IONVRAMController *_nvramController;
- IODTNVRAM *_provider;
- NVRAMVersion _version;
- uint32_t _generation;
-
- uint8_t *_nvramImage;
-
- uint32_t _commonPartitionOffset;
- uint32_t _commonPartitionSize;
-
- uint32_t _systemPartitionOffset;
- uint32_t _systemPartitionSize;
-
- OSSharedPtr<OSDictionary> _varDict;
-
- uint32_t _commonUsed;
- uint32_t _systemUsed;
-
- IORWLock *_variableLock;
- IOLock *_controllerLock;
-
- uint32_t findCurrentBank(uint32_t *gen);
- IOReturn unserializeImage(const uint8_t *image, IOByteCount length);
- IOReturn serializeVariables(void);
-
- IOReturn reloadInternal(void);
- IOReturn setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object);
-
- static OSSharedPtr<OSData> unescapeBytesToData(const uint8_t *bytes, uint32_t length);
- static OSSharedPtr<OSData> escapeDataToData(OSData * value);
-
- static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength,
- LIBKERN_RETURNS_RETAINED const OSSymbol **propSymbol, LIBKERN_RETURNS_RETAINED OSObject **propObject);
- static bool convertPropToObject(const uint8_t *propName, uint32_t propNameLength, const uint8_t *propData, uint32_t propDataLength,
- OSSharedPtr<const OSSymbol>& propSymbol, OSSharedPtr<OSObject>& propObject);
- static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const OSSymbol *propSymbol, OSObject *propObject);
- static bool convertObjectToProp(uint8_t *buffer, uint32_t *length, const char *propSymbol, OSObject *propObject);
-
-public:
- virtual
- ~IONVRAMCHRPHandler() APPLE_KEXT_OVERRIDE;
- IONVRAMCHRPHandler();
- static bool isValidImage(const uint8_t *image, IOByteCount length);
- static IONVRAMCHRPHandler *init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length);
-
- virtual IOReturn unserializeVariables(void) APPLE_KEXT_OVERRIDE;
- virtual IOReturn setVariable(const uuid_t varGuid, const char *variableName, OSObject *object) APPLE_KEXT_OVERRIDE;
- virtual bool setController(IONVRAMController *controller) APPLE_KEXT_OVERRIDE;
- virtual IOReturn sync(void) APPLE_KEXT_OVERRIDE;
- virtual IOReturn flush(const uuid_t guid, IONVRAMOperation op) APPLE_KEXT_OVERRIDE;
- virtual void reload(void) APPLE_KEXT_OVERRIDE;
- virtual uint32_t getGeneration(void) const APPLE_KEXT_OVERRIDE;
- virtual uint32_t getVersion(void) const APPLE_KEXT_OVERRIDE;
- virtual uint32_t getSystemUsed(void) const APPLE_KEXT_OVERRIDE;
- virtual uint32_t getCommonUsed(void) const APPLE_KEXT_OVERRIDE;
- virtual bool getSystemPartitionActive(void) const APPLE_KEXT_OVERRIDE;
- virtual IOReturn getVarDict(OSSharedPtr<OSDictionary> &varDictCopy) APPLE_KEXT_OVERRIDE;
-};
-
-static const char *
-get_bank_version_string(int version)
-{
- switch (version) {
- case kNVRAMVersion1:
- return NVRAM_CHRP_APPLE_HEADER_NAME_V1;
- case kNVRAMVersion2:
- return NVRAM_CHRP_APPLE_HEADER_NAME_V2;
- default:
- return "Unknown";
- }
-}
-
-static uint32_t
-adler32(const uint8_t *buffer, size_t length)
-{
- uint32_t offset;
- uint32_t adler, lowHalf, highHalf;
-
- lowHalf = 1;
- highHalf = 0;
-
- for (offset = 0; offset < length; offset++) {
- if ((offset % 5000) == 0) {
- lowHalf %= 65521L;
- highHalf %= 65521L;
- }
-
- lowHalf += buffer[offset];
- highHalf += lowHalf;
- }
-
- lowHalf %= 65521L;
- highHalf %= 65521L;
-
- adler = (highHalf << 16) | lowHalf;
-
- return adler;
-}
-
-static uint32_t
-nvram_get_adler(uint8_t *buf, int version)
-{
- return ((struct apple_nvram_header *)buf)->adler;
-}
-
-static uint32_t
-adler32_with_version(const uint8_t *buf, size_t len, int version)
-{
- size_t offset;
-
- switch (version) {
- case kNVRAMVersion1:
- case kNVRAMVersion2:
- offset = offsetof(struct apple_nvram_header, generation);
- break;
- default:
- return 0;
- }
-
- return adler32(buf + offset, len - offset);
-}
-
-static uint8_t
-chrp_checksum(const struct chrp_nvram_header *hdr)
-{
- uint16_t sum;
- const uint8_t *p;
- const uint8_t *begin = (const uint8_t *)hdr + offsetof(struct chrp_nvram_header, len);
- const uint8_t *end = (const uint8_t *)hdr + offsetof(struct chrp_nvram_header, data);
-
- // checksum the header (minus the checksum itself)
- sum = hdr->sig;
- for (p = begin; p < end; p++) {
- sum += *p;
- }
- while (sum > 0xff) {
- sum = (sum & 0xff) + (sum >> 8);
- }
-
- return sum & 0xff;
-}
-
-static IOReturn
-nvram_validate_header_v1v2(const uint8_t * buf, uint32_t *generation, int version)
-{
- IOReturn result = kIOReturnError;
- uint8_t checksum;
- const char *header_string = get_bank_version_string(version);
- struct chrp_nvram_header *chrp_header = (struct chrp_nvram_header *)buf;
- uint32_t local_gen = 0;
-
- require(buf != nullptr, exit);
-
- // <rdar://problem/73454488> Recovery Mode [Internal Build] 18D52-->18E141 [J307/308 Only]
- // we can only compare the first "nvram" parts of the name as some devices have additional junk from
- // a previous build likely copying past bounds of the "nvram" name in the const section
- if (memcmp(header_string, chrp_header->name, strlen(header_string)) == 0) {
- checksum = chrp_checksum(chrp_header);
- if (checksum == chrp_header->cksum) {
- result = kIOReturnSuccess;
- local_gen = ((struct apple_nvram_header*)buf)->generation;
-
- DEBUG_INFO("Found %s gen=%u\n", header_string, local_gen);
-
- if (generation) {
- *generation = local_gen;
- }
- } else {
- DEBUG_INFO("invalid checksum in header, found %#02x, expected %#02x\n", chrp_header->cksum, checksum);
- }
- } else {
- DEBUG_INFO("invalid bank for \"%s\", name = %#02x %#02x %#02x %#02x\n", header_string,
- chrp_header->name[0],
- chrp_header->name[1],
- chrp_header->name[2],
- chrp_header->name[3]);
- }
-
-exit:
- return result;
-}
-
-static void
-nvram_set_apple_header(uint8_t *buf, size_t len, uint32_t generation, int version)
-{
- if (version == kNVRAMVersion1 ||
- version == kNVRAMVersion2) {
- struct apple_nvram_header *apple_hdr = (struct apple_nvram_header *)buf;
- generation += 1;
- apple_hdr->generation = generation;
- apple_hdr->adler = adler32_with_version(buf, len, version);
- }
-}
-
-static NVRAMVersion
-validateNVRAMVersion(const uint8_t *buf, size_t len, uint32_t *generation)
-{
- NVRAMVersion version = kNVRAMVersionUnknown;
-
- if (nvram_validate_header_v1v2(buf, generation, kNVRAMVersion1) == kIOReturnSuccess) {
- version = kNVRAMVersion1;
- goto exit;
- }
-
- if (nvram_validate_header_v1v2(buf, generation, kNVRAMVersion2) == kIOReturnSuccess) {
- version = kNVRAMVersion2;
- goto exit;
- }
-
- DEBUG_INFO("Unable to determine version\n");
-
-exit:
- DEBUG_INFO("version=%u\n", version);
- return version;
-}
-
-IONVRAMCHRPHandler::~IONVRAMCHRPHandler()
-{
-}
-
-bool
-IONVRAMCHRPHandler::isValidImage(const uint8_t *image, IOByteCount length)
-{
- return validateNVRAMVersion(image, length, nullptr) != kNVRAMVersionUnknown;
-}
-
-IONVRAMCHRPHandler*
-IONVRAMCHRPHandler::init(IODTNVRAM *provider, const uint8_t *image, IOByteCount length)
-{
- bool propertiesOk;
-
- IONVRAMCHRPHandler *handler = new IONVRAMCHRPHandler();
-
- handler->_provider = provider;
-
- handler->_variableLock = IORWLockAlloc();
- require(handler->_variableLock != nullptr, exit);
-
- handler->_controllerLock = IOLockAlloc();
- require(handler->_controllerLock != nullptr, exit);
-
- propertiesOk = handler->getNVRAMProperties();
- require_action(propertiesOk, exit, DEBUG_ERROR("Unable to get NVRAM properties\n"));
- require_action(length == handler->_bankSize, exit, DEBUG_ERROR("length 0x%llx != _bankSize 0x%x\n", length, handler->_bankSize));
-
- if ((image != nullptr) && (length != 0)) {
- if (handler->unserializeImage(image, length) != kIOReturnSuccess) {
- DEBUG_ALWAYS("Unable to unserialize image, len=%#x\n", (unsigned int)length);
- }
- }
-
- return handler;
-
-exit:
- delete handler;
-
- return nullptr;
-}
-
-IONVRAMCHRPHandler::IONVRAMCHRPHandler() :
- _commonPartitionSize(0x800)
-{
-}
-
-IOReturn
-IONVRAMCHRPHandler::flush(const uuid_t guid, IONVRAMOperation op)
-{
- IOReturn ret = kIOReturnSuccess;
- bool flushSystem;
- bool flushCommon;
-
- flushSystem = getSystemPartitionActive() && (uuid_compare(guid, gAppleSystemVariableGuid) == 0);
- flushCommon = uuid_compare(guid, gAppleNVRAMGuid) == 0;
-
- DEBUG_INFO("flushSystem=%d, flushCommon=%d\n", flushSystem, flushCommon);
-
- NVRAMWRITELOCK(_variableLock);
- if (flushSystem || flushCommon) {
- const OSSymbol *canonicalKey;
- OSSharedPtr<OSDictionary> dictCopy;
- OSSharedPtr<OSCollectionIterator> iter;
- uuid_string_t uuidString;
-
- dictCopy = OSDictionary::withDictionary(_varDict.get());
- iter = OSCollectionIterator::withCollection(dictCopy.get());
- require_action(dictCopy && iter, exit, ret = kIOReturnNoMemory);
-
- while ((canonicalKey = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
- const char *varName;
- uuid_t varGuid;
- bool clear;
-
- parseVariableName(canonicalKey->getCStringNoCopy(), &varGuid, &varName);
-
- uuid_unparse(varGuid, uuidString);
-
- clear = ((flushSystem && (uuid_compare(varGuid, gAppleSystemVariableGuid) == 0)) ||
- (flushCommon && (uuid_compare(varGuid, gAppleSystemVariableGuid) != 0))) &&
- verifyPermission(op, varGuid, varName, getSystemPartitionActive(), true);
-
- if (clear) {
- DEBUG_INFO("Clearing entry for %s:%s\n", uuidString, varName);
- setVariableInternal(varGuid, varName, nullptr);
- } else {
- DEBUG_INFO("Keeping entry for %s:%s\n", uuidString, varName);
- }
- }
- }
-
-exit:
- NVRAMRWUNLOCK(_variableLock);
- return ret;
-}
-
-IOReturn
-IONVRAMCHRPHandler::reloadInternal(void)
-{
- uint32_t controllerBank;
- uint32_t controllerGen;
-
- NVRAMLOCKASSERTHELD(_controllerLock);
-
- controllerBank = findCurrentBank(&controllerGen);
-
- if (_currentBank != controllerBank) {
- DEBUG_ERROR("_currentBank 0x%x != controllerBank 0x%x\n", _currentBank, controllerBank);
- }
-
- if (_generation != controllerGen) {
- DEBUG_ERROR("_generation 0x%x != controllerGen 0x%x\n", _generation, controllerGen);
- }
-
- _currentBank = controllerBank;
- _generation = controllerGen;
-
- return kIOReturnSuccess;
-}
-
-void
-IONVRAMCHRPHandler::reload(void)
-{
- _reload = true;
-
- DEBUG_INFO("reload marked\n");
-}
-
-IOReturn
-IONVRAMCHRPHandler::unserializeImage(const uint8_t *image, IOByteCount length)
-{
- IOReturn ret = kIOReturnInvalid;
- uint32_t partitionOffset, partitionLength;
- uint32_t currentLength, currentOffset = 0;
- uint32_t hdr_adler, calculated_adler;
-
- _commonPartitionOffset = 0xFFFFFFFF;
- _systemPartitionOffset = 0xFFFFFFFF;
-
- _version = validateNVRAMVersion(image, _bankSize, &_generation);
- require(_version != kNVRAMVersionUnknown, exit);
-
- if (_nvramImage) {
- IOFreeData(_nvramImage, _bankSize);
- }
-
- _nvramImage = IONewData(uint8_t, length);
- _bankSize = (uint32_t)length;
- bcopy(image, _nvramImage, _bankSize);
-
- hdr_adler = nvram_get_adler(_nvramImage, _version);
- calculated_adler = adler32_with_version(_nvramImage, _bankSize, _version);
-
- if (hdr_adler != calculated_adler) {
- panic("header adler %#08X != calculated_adler %#08X\n", hdr_adler, calculated_adler);
- }
-
- // Look through the partitions to find the common and system partitions.
- while (currentOffset < _bankSize) {
- bool common_partition;
- bool system_partition;
- const chrp_nvram_header_t * header = (chrp_nvram_header_t *)(_nvramImage + currentOffset);
- const uint8_t common_v1_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_COMMON_V1};
- const uint8_t common_v2_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_COMMON_V2};
- const uint8_t system_v1_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_SYSTEM_V1};
- const uint8_t system_v2_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_SYSTEM_V2};
-
- currentLength = header->len * NVRAM_CHRP_LENGTH_BLOCK_SIZE;
-
- if (currentLength < sizeof(chrp_nvram_header_t)) {
- break;
- }
-
- partitionOffset = currentOffset + sizeof(chrp_nvram_header_t);
- partitionLength = currentLength - sizeof(chrp_nvram_header_t);
-
- if ((partitionOffset + partitionLength) > _bankSize) {
- break;
- }
-
- common_partition = (memcmp(header->name, common_v1_name, sizeof(header->name)) == 0) ||
- (memcmp(header->name, common_v2_name, sizeof(header->name)) == 0);
- system_partition = (memcmp(header->name, system_v1_name, sizeof(header->name)) == 0) ||
- (memcmp(header->name, system_v2_name, sizeof(header->name)) == 0);
-
- if (common_partition) {
- _commonPartitionOffset = partitionOffset;
- _commonPartitionSize = partitionLength;
- } else if (system_partition) {
- _systemPartitionOffset = partitionOffset;
- _systemPartitionSize = partitionLength;
- }
- currentOffset += currentLength;
- }
-
- ret = kIOReturnSuccess;
-
-exit:
- _varDict = OSDictionary::withCapacity(1);
-
- DEBUG_ALWAYS("NVRAM : commonPartitionOffset - %#x, commonPartitionSize - %#x, systemPartitionOffset - %#x, systemPartitionSize - %#x\n",
- _commonPartitionOffset, _commonPartitionSize, _systemPartitionOffset, _systemPartitionSize);
-
- return ret;
-}
-
-IOReturn
-IONVRAMCHRPHandler::unserializeVariables(void)
-{
- IOReturn ret = kIOReturnSuccess;
- uint32_t cnt, cntStart;
- const uint8_t *propName, *propData;
- uint32_t propNameLength, propDataLength, regionIndex;
- OSSharedPtr<const OSSymbol> propSymbol;
- OSSharedPtr<OSObject> propObject;
- NVRAMRegionInfo *currentRegion;
- NVRAMRegionInfo variableRegions[] = { { kIONVRAMPartitionCommon, _commonPartitionOffset, _commonPartitionSize},
- { kIONVRAMPartitionSystem, _systemPartitionOffset, _systemPartitionSize} };
-
- DEBUG_INFO("...\n");
-
- for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
- currentRegion = &variableRegions[regionIndex];
- const uint8_t * imageData = _nvramImage + currentRegion->offset;
-
- if (currentRegion->size == 0) {
- continue;
- }
-
- DEBUG_INFO("region = %d\n", currentRegion->type);
- cnt = 0;
- while (cnt < currentRegion->size) {
- cntStart = cnt;
- // Break if there is no name.
- if (imageData[cnt] == '\0') {
- break;
- }
-
- // Find the length of the name.
- propName = imageData + cnt;
- for (propNameLength = 0; (cnt + propNameLength) < currentRegion->size;
- propNameLength++) {
- if (imageData[cnt + propNameLength] == '=') {
- break;
- }
- }
-
- // Break if the name goes past the end of the partition.
- if ((cnt + propNameLength) >= currentRegion->size) {
- break;
- }
- cnt += propNameLength + 1;
-
- propData = imageData + cnt;
- for (propDataLength = 0; (cnt + propDataLength) < currentRegion->size;
- propDataLength++) {
- if (imageData[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)) {
- OSSharedPtr<const OSSymbol> canonicalKey;
- const char *varName = propSymbol.get()->getCStringNoCopy();
- uint32_t variableLength = cnt - cntStart;
-
- DEBUG_INFO("adding %s, variableLength=%#x,dataLength=%#x\n", varName, variableLength, propDataLength);
-
- if (currentRegion->type == kIONVRAMPartitionCommon) {
- canonicalKey = keyWithGuidAndCString(gAppleNVRAMGuid, varName);
- } else if (currentRegion->type == kIONVRAMPartitionSystem) {
- canonicalKey = keyWithGuidAndCString(gAppleSystemVariableGuid, varName);
- }
-
- DEBUG_INFO("adding %s, dataLength=%u\n", varName, propDataLength);
- _varDict->setObject(canonicalKey.get(), propObject.get());
- if (_provider->_diags) {
- _provider->_diags->logVariable(currentRegion->type,
- kIONVRAMOperationInit, varName,
- (void *)(uintptr_t)propDataLength,
- (void *)(uintptr_t)cntStart);
- }
-
- if (currentRegion->type == kIONVRAMPartitionSystem) {
- _systemUsed += variableLength;
- } else if (currentRegion->type == kIONVRAMPartitionCommon) {
- _commonUsed += variableLength;
- }
- }
- }
- }
-
- DEBUG_ALWAYS("_commonSize %#x, _systemSize %#x\n", _commonPartitionSize, _systemPartitionSize);
-
- ret = handleEphDM();
- verify_noerr_action(ret, panic("handleEphDM failed with ret=%08x", ret));
-
- DEBUG_INFO("_commonUsed %#x, _systemUsed %#x\n", _commonUsed, _systemUsed);
-
- if (_provider->_diags) {
- OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
- _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
- DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed());
-
- val = OSNumber::withNumber(getCommonUsed(), 32);
- _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
- DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed());
- }
-
- // Create the boot-args property if it is not in the dictionary.
- if (_provider->getProperty(kIONVRAMBootArgsKey) == nullptr) {
- propSymbol = OSSymbol::withCString(kIONVRAMBootArgsKey);
- propObject = OSString::withCStringNoCopy("");
-
- _provider->setProperty(propSymbol.get(), propObject.get());
- }
-
- _newData = true;
-
- DEBUG_INFO("%s _varDict=%p\n", __FUNCTION__, _varDict ? _varDict.get() : nullptr);
-
- return ret;
-}
-
-IOReturn
-IONVRAMCHRPHandler::serializeVariables(void)
-{
- IOReturn ret;
- bool ok = false;
- uint32_t length, maxLength, regionIndex;
- uint8_t *buffer, *tmpBuffer;
- const OSSymbol *tmpSymbol;
- OSObject *tmpObject;
- OSSharedPtr<OSCollectionIterator> iter;
- OSSharedPtr<OSNumber> generation;
- uint8_t *nvramImage;
- NVRAMRegionInfo *currentRegion;
- NVRAMRegionInfo variableRegions[] = { { kIONVRAMPartitionCommon, _commonPartitionOffset, _commonPartitionSize},
- { kIONVRAMPartitionSystem, _systemPartitionOffset, _systemPartitionSize} };
- NVRAMLOCKASSERTHELD(_controllerLock);
-
- require_action(_nvramController != nullptr, exit, (ret = kIOReturnNotReady, DEBUG_ERROR("No _nvramController\n")));
- require_action(_newData == true, exit, (ret = kIOReturnSuccess, DEBUG_INFO("No _newData to sync\n")));
- require_action(_bankSize != 0, exit, (ret = kIOReturnSuccess, DEBUG_INFO("No nvram size info\n")));
- require_action(_nvramImage != nullptr, exit, (ret = kIOReturnSuccess, DEBUG_INFO("No nvram image info\n")));
-
- nvramImage = IONewZeroData(uint8_t, _bankSize);
- require_action(nvramImage != nullptr, exit, (ret = kIOReturnNoMemory, DEBUG_ERROR("Can't create NVRAM image copy\n")));
-
- DEBUG_INFO("...\n");
-
- bcopy(_nvramImage, nvramImage, _bankSize);
-
- NVRAMREADLOCK(_variableLock);
- for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
- currentRegion = &variableRegions[regionIndex];
-
- if (currentRegion->size == 0) {
- continue;
- }
-
- DEBUG_INFO("region = %d\n", currentRegion->type);
- buffer = tmpBuffer = nvramImage + currentRegion->offset;
-
- bzero(buffer, currentRegion->size);
-
- ok = true;
- maxLength = currentRegion->size;
-
- iter = OSCollectionIterator::withCollection(_varDict.get());
- if (iter == nullptr) {
- ok = false;
- }
-
- while (ok) {
- uuid_t entryGuid;
- const char *entryName;
-
- tmpSymbol = OSDynamicCast(OSSymbol, iter->getNextObject());
-
- if (tmpSymbol == nullptr) {
- break;
- }
-
- DEBUG_INFO("_varDict entry %s\n", tmpSymbol->getCStringNoCopy());
-
- parseVariableName(tmpSymbol, &entryGuid, &entryName);
-
- if (getSystemPartitionActive()) {
- if (currentRegion->type == kIONVRAMPartitionSystem) {
- if (uuid_compare(entryGuid, gAppleSystemVariableGuid) != 0) {
- DEBUG_INFO("Skipping %s because not system var\n", entryName);
- continue;
- }
- } else if (currentRegion->type == kIONVRAMPartitionCommon) {
- if (uuid_compare(entryGuid, gAppleSystemVariableGuid) == 0) {
- DEBUG_INFO("Skipping %s for common region\n", entryName);
- continue;
- }
- }
- }
-
- DEBUG_INFO("adding variable %s\n", entryName);
-
- tmpObject = _varDict->getObject(tmpSymbol);
-
- length = maxLength;
- ok = convertObjectToProp(tmpBuffer, &length, entryName, tmpObject);
- if (ok) {
- tmpBuffer += length;
- maxLength -= length;
- }
- }
-
- if (!ok) {
- ret = kIOReturnNoSpace;
- IODeleteData(nvramImage, uint8_t, _bankSize);
- break;
- }
-
- if (currentRegion->type == kIONVRAMPartitionSystem) {
- _systemUsed = (uint32_t)(tmpBuffer - buffer);
- } else if (currentRegion->type == kIONVRAMPartitionCommon) {
- _commonUsed = (uint32_t)(tmpBuffer - buffer);
- }
- }
- NVRAMRWUNLOCK(_variableLock);
-
- DEBUG_INFO("ok=%d\n", ok);
- require(ok, exit);
-
- nvram_set_apple_header(nvramImage, _bankSize, ++_generation, _version);
-
- _currentBank = (_currentBank + 1) % _bankCount;
-
- ret = _nvramController->select(_currentBank);
- DEBUG_IFERROR(ret, "_currentBank=%#x, select=%#x\n", _currentBank, ret);
-
- ret = _nvramController->eraseBank();
- DEBUG_IFERROR(ret, "eraseBank=%#x\n", ret);
-
- ret = _nvramController->write(0, nvramImage, _bankSize);
- DEBUG_IFERROR(ret, "write=%#x\n", ret);
-
- _nvramController->sync();
-
- if (_nvramImage) {
- IODeleteData(_nvramImage, uint8_t, _bankSize);
- }
-
- _nvramImage = nvramImage;
-
- _newData = false;
-
-exit:
- return ret;
-}
-
-IOReturn
-IONVRAMCHRPHandler::setVariableInternal(const uuid_t varGuid, const char *variableName, OSObject *object)
-{
- uint32_t newSize = 0;
- uint32_t existingSize = 0;
- bool remove = (object == nullptr);
- OSObject *existing;
- OSSharedPtr<const OSSymbol> canonicalKey;
- bool systemVar;
-
- // Anyone calling setVariableInternal should've already held the lock for write.
- NVRAMRWLOCKASSERTEXCLUSIVE(_variableLock);
-
- systemVar = (uuid_compare(varGuid, gAppleSystemVariableGuid) == 0);
- canonicalKey = keyWithGuidAndCString(varGuid, variableName);
-
- if ((existing = _varDict->getObject(canonicalKey.get()))) {
- convertObjectToProp(nullptr, &existingSize, variableName, existing);
- }
-
- if (remove == false) {
- convertObjectToProp(nullptr, &newSize, variableName, object);
-
- DEBUG_INFO("setting %s, systemVar=%d, existingSize=%u, newSize=%u\n", canonicalKey.get()->getCStringNoCopy(), systemVar, existingSize, newSize);
-
- if (systemVar) {
- if ((newSize + _systemUsed - existingSize) > _systemPartitionSize) {
- DEBUG_ERROR("No space left in system partition, need=%#x, _systemUsed=%#x, _systemPartitionSize=%#x\n",
- newSize, _systemUsed, _systemPartitionSize);
- return kIOReturnNoSpace;
- } else {
- _systemUsed = _systemUsed + newSize - existingSize;
- }
- } else {
- if ((newSize + _commonUsed - existingSize) > _commonPartitionSize) {
- DEBUG_ERROR("No space left in common partition, need=%#x, _commonUsed=%#x, _commonPartitionSize=%#x\n",
- newSize, _commonUsed, _commonPartitionSize);
- return kIOReturnNoSpace;
- } else {
- _commonUsed = _commonUsed + newSize - existingSize;
- }
- }
-
- _varDict->setObject(canonicalKey.get(), object);
-
- if (_provider->_diags) {
- _provider->_diags->logVariable(getPartitionTypeForGUID(varGuid),
- kIONVRAMOperationWrite, variableName,
- (void *)(uintptr_t)newSize);
- }
- } else {
- DEBUG_INFO("removing %s, systemVar=%d, existingSize=%u\n", canonicalKey.get()->getCStringNoCopy(), systemVar, existingSize);
-
- if (systemVar) {
- _systemUsed -= existingSize;
- } else {
- _commonUsed -= existingSize;
- }
-
- _varDict->removeObject(canonicalKey.get());
-
- if (_provider->_diags) {
- _provider->_diags->logVariable(getPartitionTypeForGUID(varGuid),
- kIONVRAMOperationDelete, variableName);
- }
- }
-
- if (_provider->_diags) {
- OSSharedPtr<OSNumber> val = OSNumber::withNumber(getSystemUsed(), 32);
- _provider->_diags->setProperty(kNVRAMSystemUsedKey, val.get());
- DEBUG_INFO("%s=%u\n", kNVRAMSystemUsedKey, getSystemUsed());
-
- val = OSNumber::withNumber(getCommonUsed(), 32);
- _provider->_diags->setProperty(kNVRAMCommonUsedKey, val.get());
- DEBUG_INFO("%s=%u\n", kNVRAMCommonUsedKey, getCommonUsed());
- }
-
- _newData = true;
-
- return kIOReturnSuccess;
-}
-
-IOReturn
-IONVRAMCHRPHandler::setVariable(const uuid_t varGuid, const char *variableName, OSObject *object)
-{
- uuid_t destGuid;
- IOReturn ret = kIOReturnError;
-
- if (getSystemPartitionActive()) {
- // System region case, if they're using the GUID directly or it's on the system allow list
- // force it to use the System GUID
- if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) {
- uuid_copy(destGuid, gAppleSystemVariableGuid);
- } else {
- uuid_copy(destGuid, varGuid);
- }
- } else {
- // No system region, store System GUID as Common GUID
- if ((uuid_compare(varGuid, gAppleSystemVariableGuid) == 0) || variableInAllowList(variableName)) {
- uuid_copy(destGuid, gAppleNVRAMGuid);
- } else {
- uuid_copy(destGuid, varGuid);
- }
- }
- NVRAMWRITELOCK(_variableLock);
- ret = setVariableInternal(destGuid, variableName, object);
- NVRAMRWUNLOCK(_variableLock);
-
- return ret;
-}
-
-uint32_t
-IONVRAMCHRPHandler::findCurrentBank(uint32_t *gen)
-{
- struct apple_nvram_header storeHeader;
- uint32_t maxGen = 0;
- uint32_t currentBank = 0;
-
- NVRAMLOCKASSERTHELD(_controllerLock);
-
- for (unsigned int i = 0; i < _bankCount; i++) {
- NVRAMVersion bankVer;
- uint32_t bankGen = 0;
-
- _nvramController->select(i);
- _nvramController->read(0, (uint8_t *)&storeHeader, sizeof(storeHeader));
- bankVer = validateNVRAMVersion((uint8_t *)&storeHeader, sizeof(storeHeader), &bankGen);
-
- if ((bankVer != kNVRAMVersionUnknown) && (bankGen >= maxGen)) {
- currentBank = i;
- maxGen = bankGen;
- }
- }
-
- DEBUG_ALWAYS("currentBank=%#x, gen=%#x\n", currentBank, maxGen);
-
- *gen = maxGen;
- return currentBank;
-}
-
-bool
-IONVRAMCHRPHandler::setController(IONVRAMController *controller)
-{
- IOReturn ret;
-
- NVRAMLOCK(_controllerLock);
-
- if (_nvramController == NULL) {
- _nvramController = controller;
- }
-
- DEBUG_INFO("Controller name: %s\n", _nvramController->getName());
-
- ret = reloadInternal();
- if (ret != kIOReturnSuccess) {
- DEBUG_ERROR("reloadInternal failed, ret=0x%08x\n", ret);
- }
-
- NVRAMUNLOCK(_controllerLock);
-
- return true;
-}
-
-IOReturn
-IONVRAMCHRPHandler::sync(void)
-{
- IOReturn ret;
-
- NVRAMLOCK(_controllerLock);
-
- if (_reload) {
- ret = reloadInternal();
- require_noerr_action(ret, exit, DEBUG_ERROR("Reload failed, ret=%#x", ret));
-
- _reload = false;
- }
-
- ret = serializeVariables();
- require_noerr_action(ret, exit, DEBUG_ERROR("serializeVariables failed, ret=%#x", ret));
-
-exit:
- NVRAMUNLOCK(_controllerLock);
- return ret;
-}
-
-uint32_t
-IONVRAMCHRPHandler::getGeneration(void) const
-{
- return _generation;
-}
-
-uint32_t
-IONVRAMCHRPHandler::getVersion(void) const
-{
- return _version;
-}
-
-uint32_t
-IONVRAMCHRPHandler::getSystemUsed(void) const
-{
- return _systemUsed;
-}
-
-uint32_t
-IONVRAMCHRPHandler::getCommonUsed(void) const
-{
- return _commonUsed;
-}
-
-bool
-IONVRAMCHRPHandler::getSystemPartitionActive(void) const
-{
- return _systemPartitionSize != 0;
-}
-
-OSSharedPtr<OSData>
-IONVRAMCHRPHandler::unescapeBytesToData(const uint8_t *bytes, uint32_t length)
-{
- OSSharedPtr<OSData> data;
- uint32_t totalLength = 0;
- uint32_t offset, offset2;
- uint8_t byte;
- bool ok;
-
- // Calculate the actual length of the data.
- ok = true;
- totalLength = 0;
- for (offset = 0; offset < length;) {
- byte = bytes[offset++];
- if (byte == 0xFF) {
- byte = bytes[offset++];
- if (byte == 0x00) {
- ok = false;
- break;
- }
- offset2 = byte & 0x7F;
- } else {
- offset2 = 1;
- }
- totalLength += offset2;
- }
-
- if (ok) {
- // Create an empty OSData of the correct size.
- data = OSData::withCapacity(totalLength);
- if (data != nullptr) {
- for (offset = 0; offset < length;) {
- byte = bytes[offset++];
- if (byte == 0xFF) {
- byte = bytes[offset++];
- offset2 = byte & 0x7F;
- byte = (byte & 0x80) ? 0xFF : 0x00;
- } else {
- offset2 = 1;
- }
- data->appendByte(byte, offset2);
- }
- }
- }
-
- return data;
-}
-
-OSSharedPtr<OSData>
-IONVRAMCHRPHandler::escapeDataToData(OSData * value)
-{
- OSSharedPtr<OSData> result;
- OSBoundedPtr<const uint8_t> startPtr;
- const uint8_t *endPtr;
- const uint8_t *valueBytesPtr;
- OSBoundedPtr<const uint8_t> wherePtr;
- uint8_t byte;
- bool ok = true;
-
- valueBytesPtr = (const uint8_t *) value->getBytesNoCopy();
- endPtr = valueBytesPtr + value->getLength();
- wherePtr = OSBoundedPtr<const uint8_t>(valueBytesPtr, valueBytesPtr, endPtr);
-
- 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) < 0x7F) && (wherePtr < endPtr) && (byte == *wherePtr);
- wherePtr++) {
- }
- ok &= result->appendByte(0xff, 1);
- byte = (byte & 0x80) | ((uint8_t)(wherePtr - startPtr));
- }
- ok &= result->appendByte(byte, 1);
- }
- ok &= result->appendByte(0, 1);
-
- if (!ok) {
- result.reset();
- }
-
- return result;
-}
-
-bool
-IONVRAMCHRPHandler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength,
- const uint8_t *propData, uint32_t propDataLength,
- const OSSymbol **propSymbol,
- OSObject **propObject)
-{
- OSSharedPtr<const OSString> delimitedName;
- OSSharedPtr<const OSSymbol> tmpSymbol;
- OSSharedPtr<OSNumber> tmpNumber;
- OSSharedPtr<OSString> tmpString;
- OSSharedPtr<OSObject> tmpObject = nullptr;
-
- delimitedName = OSString::withCString((const char *)propName, propNameLength);
- tmpSymbol = OSSymbol::withString(delimitedName.get());
-
- 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, propDataLength);
- 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
-IONVRAMCHRPHandler::convertPropToObject(const uint8_t *propName, uint32_t propNameLength,
- const uint8_t *propData, uint32_t propDataLength,
- OSSharedPtr<const OSSymbol>& propSymbol,
- OSSharedPtr<OSObject>& propObject)
-{
- const OSSymbol* propSymbolRaw = nullptr;
- OSObject* propObjectRaw = nullptr;
-
- bool ok = convertPropToObject(propName, propNameLength, propData, propDataLength,
- &propSymbolRaw, &propObjectRaw);
-
- propSymbol.reset(propSymbolRaw, OSNoRetain);
- propObject.reset(propObjectRaw, OSNoRetain);
-
- return ok;
-}
-
-bool
-IONVRAMCHRPHandler::convertObjectToProp(uint8_t *buffer, uint32_t *length,
- const OSSymbol *propSymbol, OSObject *propObject)
-{
- return convertObjectToProp(buffer, length, propSymbol->getCStringNoCopy(), propObject);
-}
-
-bool
-IONVRAMCHRPHandler::convertObjectToProp(uint8_t *buffer, uint32_t *length,
- const char *propName, OSObject *propObject)
-{
- uint32_t propNameLength, propDataLength, remaining, offset;
- IONVRAMVariableType propType;
- OSBoolean *tmpBoolean = nullptr;
- OSNumber *tmpNumber = nullptr;
- OSString *tmpString = nullptr;
- OSSharedPtr<OSData> tmpData;
-
- propNameLength = (uint32_t)strlen(propName);
- propType = getVariableType(propName);
- offset = 0;
- remaining = 0;
-
- // 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());
- // escapeDataToData() adds the NULL byte to the data
- // subtract 1 here to keep offset consistent with the other cases
- propDataLength = tmpData->getLength() - 1;
- }
- break;
-
- default:
- break;
- }
-
- // Make sure the propertySize is known and will fit.
- if (propDataLength == 0xFFFFFFFF) {
- return false;
- }
-
- if (buffer) {
- // name + '=' + data + '\0'
- if ((propNameLength + propDataLength + 2) > *length) {
- return false;
- }
-
- remaining = *length;
- }
-
- *length = 0;
-
- // Copy the property name equal sign.
- offset += snprintf((char *)buffer, remaining, "%s=", propName);
- if (buffer) {
- if (remaining > offset) {
- buffer += offset;
- remaining = remaining - offset;
- } else {
- return false;
- }
- }
-
- switch (propType) {
- case kOFVariableTypeBoolean:
- if (tmpBoolean->getValue()) {
- offset += strlcpy((char *)buffer, "true", remaining);
- } else {
- offset += strlcpy((char *)buffer, "false", remaining);
- }
- break;
-
- case kOFVariableTypeNumber:
- {
- uint32_t tmpValue = tmpNumber->unsigned32BitValue();
- if (tmpValue == 0xFFFFFFFF) {
- offset += strlcpy((char *)buffer, "-1", remaining);
- } else if (tmpValue < 1000) {
- offset += snprintf((char *)buffer, remaining, "%d", (uint32_t)tmpValue);
- } else {
- offset += snprintf((char *)buffer, remaining, "%#x", (uint32_t)tmpValue);
- }
- }
- break;
-
- case kOFVariableTypeString:
- offset += strlcpy((char *)buffer, tmpString->getCStringNoCopy(), remaining);
- break;
-
- case kOFVariableTypeData:
- if (buffer) {
- bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength);
- }
- tmpData.reset();
- offset += propDataLength;
- break;
-
- default:
- break;
- }
-
- *length = offset + 1;
-
- return true;
-}
-
-IOReturn
-IONVRAMCHRPHandler::getVarDict(OSSharedPtr<OSDictionary> &varDictCopy)
-{
- IOReturn ret = kIOReturnNotFound;
-
- NVRAMREADLOCK(_variableLock);
- if (_varDict) {
- varDictCopy = OSDictionary::withDictionary(_varDict.get());
- if (varDictCopy) {
- if (OSDictionary::withCapacity(varDictCopy->getCount()) != nullptr) {
- ret = kIOReturnSuccess;
- }
- }
- }
- NVRAMRWUNLOCK(_variableLock);
-
- return ret;
-}