X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/316670eb35587141e969394ae8537d66b9211e80..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/iokit/Kernel/IONVRAM.cpp diff --git a/iokit/Kernel/IONVRAM.cpp b/iokit/Kernel/IONVRAM.cpp index 51a72cfb1..69725d403 100644 --- a/iokit/Kernel/IONVRAM.cpp +++ b/iokit/Kernel/IONVRAM.cpp @@ -1,8 +1,9 @@ /* * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2007-2012 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 @@ -11,10 +12,10 @@ * 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, @@ -22,1676 +23,2419 @@ * 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@ */ +#define IOKIT_ENABLE_SHARED_PTR + +#include #include #include #include #include #include +#include +#include #include +#include #include #define super IOService -#define kIONVRAMPrivilege kIOClientPrivilegeAdministrator -//#define kIONVRAMPrivilege kIOClientPrivilegeLocalUser +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +// Internal values +#define NVRAM_CHRP_SIG_APPLE 0x5A +#define NVRAM_CHRP_APPLE_HEADER_NAME "nvram" + +// From Apple CHRP Spec +#define NVRAM_CHRP_SIG_SYSTEM 0x70 +#define NVRAM_CHRP_SIG_CONFIG 0x71 +#define NVRAM_CHRP_SIG_FREESPACE 0x7F + +#define NVRAM_CHRP_PARTITION_NAME_COMMON "common" +#define NVRAM_CHRP_PARTITION_NAME_SYSTEM "system" +#define NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY "secure" +#define NVRAM_CHRP_PARTITION_NAME_FREESPACE "\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77\x77" + +#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; + + +#define kIONVRAMPrivilege kIOClientPrivilegeAdministrator OSDefineMetaClassAndStructors(IODTNVRAM, IOService); -bool IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane) -{ - OSDictionary *dict; - - if (!super::init(old, plane)) return false; - - dict = OSDictionary::withCapacity(1); - if (dict == 0) return false; - setPropertyTable(dict); - - _nvramImage = IONew(UInt8, kIODTNVRAMImageSize); - if (_nvramImage == 0) return false; - - _nvramPartitionOffsets = OSDictionary::withCapacity(1); - if (_nvramPartitionOffsets == 0) return false; - - _nvramPartitionLengths = OSDictionary::withCapacity(1); - if (_nvramPartitionLengths == 0) return false; - - _registryPropertiesKey = OSSymbol::withCStringNoCopy("aapl,pci"); - if (_registryPropertiesKey == 0) return false; - - return true; -} +#if defined(DEBUG) || defined(DEVELOPMENT) +#define DEBUG_INFO(fmt, args...) \ +({ \ + if (gNVRAMLogging) \ + IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \ +}) + +#define DEBUG_ALWAYS(fmt, args...) \ +({ \ + IOLog("IONVRAM::%s:%u - " fmt, __FUNCTION__, __LINE__, ##args); \ +}) +#else +#define DEBUG_INFO(fmt, args...) +#define DEBUG_ALWAYS(fmt, args...) +#endif -void IODTNVRAM::registerNVRAMController(IONVRAMController *nvram) -{ - char partitionID[18]; - UInt32 partitionOffset, partitionLength; - UInt32 freePartitionOffset, freePartitionSize; - UInt32 currentLength, currentOffset = 0; - OSNumber *partitionOffsetNumber, *partitionLengthNumber; - - if (_nvramController != 0) return; - - _nvramController = nvram; - - _nvramController->read(0, _nvramImage, kIODTNVRAMImageSize); - - // Find the offsets for the OF, XPRAM, NameRegistry and PanicInfo partitions. - _ofPartitionOffset = 0xFFFFFFFF; - _xpramPartitionOffset = 0xFFFFFFFF; - _nrPartitionOffset = 0xFFFFFFFF; - _piPartitionOffset = 0xFFFFFFFF; - freePartitionOffset = 0xFFFFFFFF; - freePartitionSize = 0; - if (getPlatform()->getBootROMType()) { - // Look through the partitions to find the OF, MacOS partitions. - while (currentOffset < kIODTNVRAMImageSize) { - currentLength = ((UInt16 *)(_nvramImage + currentOffset))[1] * 16; - - partitionOffset = currentOffset + 16; - partitionLength = currentLength - 16; - - if (strncmp((const char *)_nvramImage + currentOffset + 4, - kIODTNVRAMOFPartitionName, 12) == 0) { - _ofPartitionOffset = partitionOffset; - _ofPartitionSize = partitionLength; - } else if (strncmp((const char *)_nvramImage + currentOffset + 4, - kIODTNVRAMXPRAMPartitionName, 12) == 0) { - _xpramPartitionOffset = partitionOffset; - _xpramPartitionSize = kIODTNVRAMXPRAMSize; - _nrPartitionOffset = _xpramPartitionOffset + _xpramPartitionSize; - _nrPartitionSize = partitionLength - _xpramPartitionSize; - } else if (strncmp((const char *)_nvramImage + currentOffset + 4, - kIODTNVRAMPanicInfoPartitonName, 12) == 0) { - _piPartitionOffset = partitionOffset; - _piPartitionSize = partitionLength; - } else if (strncmp((const char *)_nvramImage + currentOffset + 4, - kIODTNVRAMFreePartitionName, 12) == 0) { - freePartitionOffset = currentOffset; - freePartitionSize = currentLength; - } else { - // Construct the partition ID from the signature and name. - snprintf(partitionID, sizeof(partitionID), "0x%02x,", - *(UInt8 *)(_nvramImage + currentOffset)); - strncpy(partitionID + 5, - (const char *)(_nvramImage + currentOffset + 4), 12); - partitionID[17] = '\0'; - - partitionOffsetNumber = OSNumber::withNumber(partitionOffset, 32); - partitionLengthNumber = OSNumber::withNumber(partitionLength, 32); - - // Save the partition offset and length - _nvramPartitionOffsets->setObject(partitionID, partitionOffsetNumber); - _nvramPartitionLengths->setObject(partitionID, partitionLengthNumber); - - partitionOffsetNumber->release(); - partitionLengthNumber->release(); - } - currentOffset += currentLength; - } - } else { - // Use the fixed address for old world machines. - _ofPartitionOffset = 0x1800; - _ofPartitionSize = 0x0800; - _xpramPartitionOffset = 0x1300; - _xpramPartitionSize = 0x0100; - _nrPartitionOffset = 0x1400; - _nrPartitionSize = 0x0400; - } - - if (_ofPartitionOffset != 0xFFFFFFFF) - _ofImage = _nvramImage + _ofPartitionOffset; - if (_xpramPartitionOffset != 0xFFFFFFFF) - _xpramImage = _nvramImage + _xpramPartitionOffset; - if (_nrPartitionOffset != 0xFFFFFFFF) - _nrImage = _nvramImage + _nrPartitionOffset; - - if (_piPartitionOffset == 0xFFFFFFFF) { - if (freePartitionSize > 0x20) { - // Set the signature to 0xa1. - _nvramImage[freePartitionOffset] = 0xa1; - // Set the checksum to 0. - _nvramImage[freePartitionOffset + 1] = 0; - // Set the name for the Panic Info partition. - strncpy((char *)(_nvramImage + freePartitionOffset + 4), - kIODTNVRAMPanicInfoPartitonName, 12); - - // Calculate the partition offset and size. - _piPartitionOffset = freePartitionOffset + 0x10; - _piPartitionSize = 0x800; - if (_piPartitionSize + 0x20 > freePartitionSize) - _piPartitionSize = freePartitionSize - 0x20; - - _piImage = _nvramImage + _piPartitionOffset; - - // Zero the new partition. - bzero(_piImage, _piPartitionSize); - - // Set the partition size. - *(UInt16 *)(_nvramImage + freePartitionOffset + 2) = - (_piPartitionSize / 0x10) + 1; - - // Set the partition checksum. - _nvramImage[freePartitionOffset + 1] = - calculatePartitionChecksum(_nvramImage + freePartitionOffset); - - // Calculate the free partition offset and size. - freePartitionOffset += _piPartitionSize + 0x10; - freePartitionSize -= _piPartitionSize + 0x10; - - // Set the signature to 0x7f. - _nvramImage[freePartitionOffset] = 0x7f; - // Set the checksum to 0. - _nvramImage[freePartitionOffset + 1] = 0; - // Set the name for the free partition. - strncpy((char *)(_nvramImage + freePartitionOffset + 4), - kIODTNVRAMFreePartitionName, 12); - // Set the partition size. - *(UInt16 *)(_nvramImage + freePartitionOffset + 2) = - freePartitionSize / 0x10; - // Set the partition checksum. - _nvramImage[freePartitionOffset + 1] = - calculatePartitionChecksum(_nvramImage + freePartitionOffset); - - // Set the nvram image as dirty. - _nvramImageDirty = true; - } - } else { - _piImage = _nvramImage + _piPartitionOffset; - } - - _lastDeviceSync = 0; - _freshInterval = TRUE; // we will allow sync() even before the first 15 minutes have passed. - - initOFVariables(); -} +#define DEBUG_ERROR DEBUG_ALWAYS + +#define CONTROLLERLOCK() \ +({ \ + if (preemption_enabled() && !panic_active()) \ + IOLockLock(_controllerLock); \ +}) + +#define CONTROLLERUNLOCK() \ +({ \ + if (preemption_enabled() && !panic_active()) \ + IOLockUnlock(_controllerLock); \ +}) + +#define NVRAMLOCK() \ +({ \ + if (preemption_enabled() && !panic_active()) \ + IOLockLock(_variableLock); \ +}) + +#define NVRAMUNLOCK() \ +({ \ + if (preemption_enabled() && !panic_active()) \ + IOLockUnlock(_variableLock); \ +}) + +#define NVRAMLOCKASSERT() \ +({ \ + if (preemption_enabled() && !panic_active()) \ + IOLockAssert(_variableLock, kIOLockAssertOwned); \ +}) + +typedef struct { + const char *name; + UInt32 offset; + UInt32 size; + OSSharedPtr &dict; + UInt8 *image; +} NVRAMRegionInfo; + +// Guid for Apple System Boot variables +// 40A0DDD2-77F8-4392-B4A3-1E7304206516 +UUID_DEFINE(gAppleSystemVariableGuid, 0x40, 0xA0, 0xDD, 0xD2, 0x77, 0xF8, 0x43, 0x92, 0xB4, 0xA3, 0x1E, 0x73, 0x04, 0x20, 0x65, 0x16); + +// Apple NVRAM Variable namespace (APPLE_VENDOR_OS_VARIABLE_GUID) +// 7C436110-AB2A-4BBB-A880-FE41995C9F82 +UUID_DEFINE(gAppleNVRAMGuid, 0x7C, 0x43, 0x61, 0x10, 0xAB, 0x2A, 0x4B, 0xBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82); + +static bool gNVRAMLogging = false; + +// allowlist variables from macboot that need to be set/get from system region if present +static const char * const gNVRAMSystemList[] = { + "adbe-tunable", + "adbe-tunables", + "adfe-tunables", + "alamo-path", + "alt-boot-volume", + "ASMB", + "atc0", + "atc1", + "auto-boot", + "auto-boot-halt-stage", + "auto-boot-once", + "auto-boot-usb", + "auxkc-path", + "backlight-level", + "backlight-nits", + "base-system-path", + "boot-args", + "boot-breadcrumbs", + "boot-command", + "boot-device", + "boot-image", + "boot-partition", + "boot-path", + "boot-ramdisk", + "boot-script", + "boot-volume", + "bootdelay", + "bt1addr", + "btaddr", + "cam-use-ext-ldo", + "CLCG_override", + "com.apple.System.boot-nonce", + "com.apple.System.rtc-offset", + "com.apple.System.tz0-size", + "core-bin-offset", + "cpu-bin-offset", + "darkboot", + "DClr_override", + "dcp-auto-boot", + "debug-gg", + "debug-soc", + "debug-uarts", + "diags-path", + "disable-boot-wdt", + "display-color-space", + "display-timing", + "display-vsh-comp", + "dpcd-max-brightness", + "dtdump", + "dtdump-path", + "e75", + "emu", + "enable-auth-debug", + "enable-jop", + "enable-marconi", + "enable-upgrade-fallback", + "enforce-iuob", + "eth1addr", + "ethaddr", + "failboot-breadcrumbs", + "fixed-lcm-boost", + "force-ctrr-lock", + "force-upgrade-fail", + "fuos-path", + "hib-ui-force", + "hibhack-test-hmac", + "iboot-data", + "iboot-failure-reason", + "iboot-failure-reason-str", + "iboot-failure-volume", + "iboot1-precommitted", + "idle-off", + "is-tethered", + "kaslr-off", + "kaslr-slide", + "kis-rsm", + "knobs", + "loadaddr", + "memmapdump", + "mipi-bridge-cmd-verify", + "mipi-bridge-poll-cmd-fifo", + "no-ctrr", + "one-time-boot-command", + "osenvironment", + "ota-breadcrumbs", + "ota-outcome", + "panicmedic", + "panicmedic-threshold", + "panicmedic-timestamps", + "phleet-path", + "pinot-panel-id", + "pintoaddr", + "policy-nonce-digests", + "preserve-debuggability", + "prevent-restores", // Keep for factory + "prev-lang:kbd", + "ramrod-kickstart-aces", + "rbdaddr0", + "rbm-path", + "reconfig-behavior", + "reconfig-breakpoints", + "recovery-boot-mode", + "recovery-breadcrumbs", + "restored-host-timeout", + "root-live-fs", + "rtos-path", + "soc-bin-offset", + "StartupMute", + "StartupMuteAccessibility", + "storage-prev-assert", + "storage-prev-assert-stored", + "summit-panel-id", + "SystemAudioVolume", + "SystemAudioVolumeExtension", + "SystemAudioVolumeSaved", + "tz0-size-override", + "upgrade-fallback-boot-command", + "upgrade-retry", + "usb-enabled", + "wifi1addr", + "wifiaddr", + nullptr +}; -void IODTNVRAM::sync(void) -{ - if (!_nvramImageDirty && !_ofImageDirty) return; - - // Don't try to sync OF Variables if the system has already paniced. - if (!_systemPaniced) syncOFVariables(); - - _nvramController->write(0, _nvramImage, kIODTNVRAMImageSize); - _nvramController->sync(); - - _nvramImageDirty = false; -} +typedef struct { + const char *name; + IONVRAMVariableType type; +} VariableTypeEntry; + +static const +VariableTypeEntry gVariableTypes[] = { + {"auto-boot?", kOFVariableTypeBoolean}, + {"boot-args", kOFVariableTypeString}, + {"boot-command", kOFVariableTypeString}, + {"boot-device", kOFVariableTypeString}, + {"boot-file", kOFVariableTypeString}, + {"boot-screen", kOFVariableTypeString}, + {"boot-script", kOFVariableTypeString}, + {"console-screen", kOFVariableTypeString}, + {"default-client-ip", kOFVariableTypeString}, + {"default-gateway-ip", kOFVariableTypeString}, + {"default-mac-address?", kOFVariableTypeBoolean}, + {"default-router-ip", kOFVariableTypeString}, + {"default-server-ip", kOFVariableTypeString}, + {"default-subnet-mask", kOFVariableTypeString}, + {"diag-device", kOFVariableTypeString}, + {"diag-file", kOFVariableTypeString}, + {"diag-switch?", kOFVariableTypeBoolean}, + {"fcode-debug?", kOFVariableTypeBoolean}, + {"input-device", kOFVariableTypeString}, + {"input-device-1", kOFVariableTypeString}, + {"little-endian?", kOFVariableTypeBoolean}, + {"load-base", kOFVariableTypeNumber}, + {"mouse-device", kOFVariableTypeString}, + {"nvramrc", kOFVariableTypeString}, + {"oem-banner", kOFVariableTypeString}, + {"oem-banner?", kOFVariableTypeBoolean}, + {"oem-logo", kOFVariableTypeString}, + {"oem-logo?", kOFVariableTypeBoolean}, + {"output-device", kOFVariableTypeString}, + {"output-device-1", kOFVariableTypeString}, + {"pci-probe-list", kOFVariableTypeNumber}, + {"pci-probe-mask", kOFVariableTypeNumber}, + {"real-base", kOFVariableTypeNumber}, + {"real-mode?", kOFVariableTypeBoolean}, + {"real-size", kOFVariableTypeNumber}, + {"screen-#columns", kOFVariableTypeNumber}, + {"screen-#rows", kOFVariableTypeNumber}, + {"security-mode", kOFVariableTypeString}, + {"selftest-#megs", kOFVariableTypeNumber}, + {"use-generic?", kOFVariableTypeBoolean}, + {"use-nvramrc?", kOFVariableTypeBoolean}, + {"virt-base", kOFVariableTypeNumber}, + {"virt-size", kOFVariableTypeNumber}, + +#if !defined(__x86_64__) + {"acc-cm-override-charger-count", kOFVariableTypeNumber}, + {"acc-cm-override-count", kOFVariableTypeNumber}, + {"acc-mb-ld-lifetime", kOFVariableTypeNumber}, + {"com.apple.System.boot-nonce", kOFVariableTypeString}, + {"darkboot", kOFVariableTypeBoolean}, + {"enter-tdm-mode", kOFVariableTypeBoolean}, +#endif /* !defined(__x86_64__) */ + {nullptr, kOFVariableTypeData} // Default type to return +}; -bool IODTNVRAM::serializeProperties(OSSerialize *s) const -{ - bool result, hasPrivilege; - UInt32 variablePerm; - const OSSymbol *key; - OSDictionary *dict; - OSCollectionIterator *iter = 0; - - // Verify permissions. - hasPrivilege = (kIOReturnSuccess == IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege)); - - dict = OSDictionary::withCapacity(1); - if (dict == 0) return false; - - if (_ofDict == 0) { - /* No nvram. Return an empty dictionary. */ - } else { - /* Copy properties with client privilege. */ - iter = OSCollectionIterator::withCollection(_ofDict); - if (iter == 0) { - dict->release(); - return false; - } - while (1) { - key = OSDynamicCast(OSSymbol, iter->getNextObject()); - if (key == 0) break; - - variablePerm = getOFVariablePerm(key); - if ((hasPrivilege || (variablePerm != kOFVariablePermRootOnly)) && - ( ! (variablePerm == kOFVariablePermKernelOnly && current_task() != kernel_task) )) { - dict->setObject(key, _ofDict->getObject(key)); - } - } - } - - result = dict->serialize(s); - - dict->release(); - if (iter != 0) iter->release(); - - return result; -} +union VariablePermission { + struct { + uint64_t UserWrite :1; + uint64_t RootRequired :1; + uint64_t KernelOnly :1; + uint64_t ResetNVRAMOnlyDelete :1; + uint64_t NeverAllowedToDelete :1; + uint64_t FullAccess :1; + uint64_t Reserved:58; + } Bits; + uint64_t Uint64; +}; + +typedef struct { + const char *name; + VariablePermission p; +} VariablePermissionEntry; + +static const +VariablePermissionEntry gVariablePermissions[] = { + {"aapl,pci", .p.Bits.RootRequired = 1}, + {"battery-health", .p.Bits.RootRequired = 1, + .p.Bits.NeverAllowedToDelete = 1}, + {"boot-image", .p.Bits.UserWrite = 1}, + {"com.apple.System.fp-state", .p.Bits.KernelOnly = 1}, + {"policy-nonce-digests", .p.Bits.ResetNVRAMOnlyDelete = 1}, + {"security-password", .p.Bits.RootRequired = 1}, + +#if !defined(__x86_64__) + {"acc-cm-override-charger-count", .p.Bits.KernelOnly = 1}, + {"acc-cm-override-count", .p.Bits.KernelOnly = 1}, + {"acc-mb-ld-lifetime", .p.Bits.KernelOnly = 1}, + {"backlight-level", .p.Bits.UserWrite = 1}, + {"com.apple.System.boot-nonce", .p.Bits.KernelOnly = 1}, + {"com.apple.System.sep.art", .p.Bits.KernelOnly = 1}, + {"darkboot", .p.Bits.UserWrite = 1}, + {"nonce-seeds", .p.Bits.KernelOnly = 1}, +#endif /* !defined(__x86_64__) */ + + {nullptr, {.Bits.FullAccess = 1}} // Default access +}; + +static IONVRAMVariableType +getVariableType(const char *propName) +{ + const VariableTypeEntry *entry; + + entry = gVariableTypes; + while (entry->name != nullptr) { + if (strcmp(entry->name, propName) == 0) { + break; + } + entry++; + } -OSObject *IODTNVRAM::getProperty(const OSSymbol *aKey) const -{ - IOReturn result; - UInt32 variablePerm; - - if (_ofDict == 0) return 0; - - // Verify permissions. - variablePerm = getOFVariablePerm(aKey); - result = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege); - if (result != kIOReturnSuccess) { - if (variablePerm == kOFVariablePermRootOnly) return 0; - } - if (variablePerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return 0; - - return _ofDict->getObject(aKey); + return entry->type; } -OSObject *IODTNVRAM::getProperty(const char *aKey) const -{ - const OSSymbol *keySymbol; - OSObject *theObject = 0; - - keySymbol = OSSymbol::withCStringNoCopy(aKey); - if (keySymbol != 0) { - theObject = getProperty(keySymbol); - keySymbol->release(); - } - - return theObject; +static IONVRAMVariableType +getVariableType(const OSSymbol *propSymbol) +{ + return getVariableType(propSymbol->getCStringNoCopy()); } -bool IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject) -{ - bool result; - UInt32 propType, propPerm; - OSString *tmpString; - OSObject *propObject = 0; - - if (_ofDict == 0) return false; - - // Verify permissions. - propPerm = getOFVariablePerm(aKey); - result = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege); - if (result != kIOReturnSuccess) { - if (propPerm != kOFVariablePermUserWrite) return false; - } - if (propPerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return 0; - - // Don't allow creation of new properties on old world machines. - if (getPlatform()->getBootROMType() == 0) { - if (_ofDict->getObject(aKey) == 0) return false; - } - - // Don't allow change of 'aapl,panic-info'. - if (aKey->isEqualTo(kIODTNVRAMPanicInfoKey)) return false; - - // Make sure the object is of the correct type. - propType = getOFVariableType(aKey); - switch (propType) { - 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) { - tmpString = OSDynamicCast(OSString, anObject); - if (tmpString != 0) { - propObject = OSData::withBytes(tmpString->getCStringNoCopy(), - tmpString->getLength()); - } - } - break; - } - - if (propObject == 0) return false; - - result = _ofDict->setObject(aKey, propObject); - - if (result) { - if (getPlatform()->getBootROMType() == 0) { - updateOWBootArgs(aKey, propObject); - } - - _ofImageDirty = true; - } - - return result; +static VariablePermission +getVariablePermission(const char *propName) +{ + const VariablePermissionEntry *entry; + + entry = gVariablePermissions; + while (entry->name != nullptr) { + if (strcmp(entry->name, propName) == 0) { + break; + } + entry++; + } + + return entry->p; } -void IODTNVRAM::removeProperty(const OSSymbol *aKey) -{ - bool result; - UInt32 propPerm; - - if (_ofDict == 0) return; - - // Verify permissions. - propPerm = getOFVariablePerm(aKey); - result = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator); - if (result != kIOReturnSuccess) { - if (propPerm != kOFVariablePermUserWrite) return; - } - if (propPerm == kOFVariablePermKernelOnly && current_task() != kernel_task) return; - - // Don't allow removal of properties on old world machines. - if (getPlatform()->getBootROMType() == 0) return; - - // Don't allow change of 'aapl,panic-info'. - if (aKey->isEqualTo(kIODTNVRAMPanicInfoKey)) return; - - // If the object exists, remove it from the dictionary. - result = _ofDict->getObject(aKey) != 0; - if (result) { - _ofDict->removeObject(aKey); - - _ofImageDirty = true; - } +static bool +variableInAllowList(const char *varName) +{ + unsigned int i = 0; + + while (gNVRAMSystemList[i] != nullptr) { + if (strcmp(varName, gNVRAMSystemList[i]) == 0) { + return true; + } + i++; + } + + return false; } -IOReturn IODTNVRAM::setProperties(OSObject *properties) -{ - bool result = true; - OSObject *object; - const OSSymbol *key; - const OSString *tmpStr; - OSDictionary *dict; - OSCollectionIterator *iter; - - dict = OSDynamicCast(OSDictionary, properties); - if (dict == 0) return kIOReturnBadArgument; - - iter = OSCollectionIterator::withCollection(dict); - if (iter == 0) return kIOReturnBadArgument; - - while (result) { - key = OSDynamicCast(OSSymbol, iter->getNextObject()); - if (key == 0) break; - - object = dict->getObject(key); - if (object == 0) continue; - - if (key->isEqualTo(kIONVRAMDeletePropertyKey)) { - tmpStr = OSDynamicCast(OSString, object); - if (tmpStr != 0) { - key = OSSymbol::withString(tmpStr); - removeProperty(key); - key->release(); - result = true; +static bool +verifyWriteSizeLimit(const uuid_t *varGuid, const char *variableName, size_t propDataSize) +{ + if (variableInAllowList(variableName)) { + if (strnstr(variableName, "breadcrumbs", strlen(variableName)) != NULL) { + return propDataSize <= 1024; } else { - result = false; + return propDataSize <= 768; } - } else if(key->isEqualTo(kIONVRAMSyncNowPropertyKey)) { - tmpStr = OSDynamicCast(OSString, object); - if (tmpStr != 0) { + } + + return true; +} - result = true; // We are not going to gaurantee sync, this is best effort +static bool +verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const char *varName) +{ + VariablePermission perm; + bool kernel, admin, writeEntitled, readEntitled, allowList, systemGuid, systemEntitled; - if(safeToSync()) - sync(); + perm = getVariablePermission(varName); - } else { - result = false; + kernel = current_task() == kernel_task; + + if (perm.Bits.KernelOnly) { + DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel); + return kernel; + } + + allowList = variableInAllowList(varName); + systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0; + admin = IOUserClient::clientHasPrivilege(current_task(), kIONVRAMPrivilege) == kIOReturnSuccess; + writeEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMWriteAccessKey); + readEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMReadAccessKey); + systemEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMSystemAllowKey) || kernel; + + switch (op) { + case kIONVRAMOperationRead: + if (kernel || admin || readEntitled || perm.Bits.FullAccess) { + return true; + } + break; + + case kIONVRAMOperationWrite: + if (kernel || perm.Bits.UserWrite || admin || writeEntitled) { + if (systemGuid) { + if (allowList) { + if (!systemEntitled) { + DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName); + } + } else if (!systemEntitled) { + DEBUG_ERROR("Not entitled for system region writes for %s\n", varName); + break; + } + } + return true; + } + break; + + case kIONVRAMOperationDelete: + case kIONVRAMOperationObliterate: + case kIONVRAMOperationReset: + if (perm.Bits.NeverAllowedToDelete) { + DEBUG_INFO("Never allowed to delete %s\n", varName); + break; + } else if ((op == kIONVRAMOperationObliterate) && perm.Bits.ResetNVRAMOnlyDelete) { + DEBUG_INFO("Not allowed to obliterate %s\n", varName); + break; + } + + if (kernel || perm.Bits.UserWrite || admin || writeEntitled) { + if (systemGuid) { + if (allowList) { + if (!systemEntitled) { + DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName); + } + } else if (!systemEntitled) { + DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName); + break; + } + } + return true; } + break; } - else { - result = setProperty(key, object); - } - } - - iter->release(); - - if (result) return kIOReturnSuccess; - else return kIOReturnError; + DEBUG_INFO("Permission for %s denied, kernel=%d, admin=%d, writeEntitled=%d, readEntitled=%d, systemGuid=%d, systemEntitled=%d\n", + varName, kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled); + return false; } -IOReturn IODTNVRAM::readXPRAM(IOByteCount offset, UInt8 *buffer, - IOByteCount length) +static bool +verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const OSSymbol *varName) { - if (_xpramImage == 0) return kIOReturnUnsupported; - - if ((buffer == 0) || (length == 0) || - (offset + length > kIODTNVRAMXPRAMSize)) - return kIOReturnBadArgument; - - bcopy(_nvramImage + _xpramPartitionOffset + offset, buffer, length); - - return kIOReturnSuccess; + return verifyPermission(op, varGuid, varName->getCStringNoCopy()); } -IOReturn IODTNVRAM::writeXPRAM(IOByteCount offset, UInt8 *buffer, - IOByteCount length) +/* + * Parse a variable name of the form "GUID:name". + * If the name cannot be parsed, substitute the Apple global variable GUID. + * Returns TRUE if a GUID was found in the name, FALSE otherwise. + * The guidResult and nameResult arguments may be nullptr if you just want + * to check the format of the string. + */ +static bool +parseVariableName(const char *key, uuid_t *guidResult, const char **nameResult) { - if (_xpramImage == 0) return kIOReturnUnsupported; - - if ((buffer == 0) || (length == 0) || - (offset + length > kIODTNVRAMXPRAMSize)) - return kIOReturnBadArgument; - - bcopy(buffer, _nvramImage + _xpramPartitionOffset + offset, length); + uuid_string_t temp = {0}; + size_t keyLen = strlen(key); + bool result = false; + const char *name = key; + uuid_t guid; + + if (keyLen > sizeof(temp)) { + // check for at least UUID + ":" + more + memcpy(temp, key, sizeof(temp) - 1); + + if ((uuid_parse(temp, guid) == 0) && + (key[sizeof(temp) - 1] == ':')) { + name = key + sizeof(temp); + result = true; + } + } + + if (guidResult) { + result ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid); + } + if (nameResult) { + *nameResult = name; + } - _nvramImageDirty = true; - - return kIOReturnSuccess; + return false; } -IOReturn IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry, - const OSSymbol **name, - OSData **value) +// private IOService based class for publishing distinct dictionary properties on +// for easy ioreg access since the serializeProperties call is overloaded and is used +// as variable access +class IODTNVRAMVariables : public IOService { - IOReturn err; + OSDeclareDefaultStructors(IODTNVRAMVariables) +private: + IODTNVRAM *_provider; + OSDictionary *_properties; + uuid_t _guid; + +public: + bool init(const uuid_t *guid); + virtual bool start(IOService * provider) APPLE_KEXT_OVERRIDE; + virtual IOReturn setProperties(OSObject * properties) APPLE_KEXT_OVERRIDE; + virtual bool serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE; +}; - if (getPlatform()->getBootROMType()) - err = readNVRAMPropertyType1(entry, name, value); - else - err = readNVRAMPropertyType0(entry, name, value); - - return err; -} +OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService) + +bool +IODTNVRAMVariables::init(const uuid_t *guid) +{ + require(super::init(), error); + require(guid, error); + + uuid_copy(_guid, *guid); + + return true; -IOReturn IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry, - const OSSymbol *name, - OSData *value) -{ - IOReturn err; - - if (getPlatform()->getBootROMType()) - err = writeNVRAMPropertyType1(entry, name, value); - else - err = writeNVRAMPropertyType0(entry, name, value); - - return err; +error: + return false; } -OSDictionary *IODTNVRAM::getNVRAMPartitions(void) +bool +IODTNVRAMVariables::start(IOService * provider) { - return _nvramPartitionLengths; -} + require(IOService::start(provider), error); -IOReturn IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID, - IOByteCount offset, UInt8 *buffer, - IOByteCount length) -{ - OSNumber *partitionOffsetNumber, *partitionLengthNumber; - UInt32 partitionOffset, partitionLength; - - partitionOffsetNumber = - (OSNumber *)_nvramPartitionOffsets->getObject(partitionID); - partitionLengthNumber = - (OSNumber *)_nvramPartitionLengths->getObject(partitionID); - - if ((partitionOffsetNumber == 0) || (partitionLengthNumber == 0)) - return kIOReturnNotFound; - - partitionOffset = partitionOffsetNumber->unsigned32BitValue(); - partitionLength = partitionLengthNumber->unsigned32BitValue(); - - if ((buffer == 0) || (length == 0) || - (offset + length > partitionLength)) - return kIOReturnBadArgument; - - bcopy(_nvramImage + partitionOffset + offset, buffer, length); - - return kIOReturnSuccess; -} + require(_provider = OSDynamicCast(IODTNVRAM, provider), error); -IOReturn IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID, - IOByteCount offset, UInt8 *buffer, - IOByteCount length) -{ - OSNumber *partitionOffsetNumber, *partitionLengthNumber; - UInt32 partitionOffset, partitionLength; - - partitionOffsetNumber = - (OSNumber *)_nvramPartitionOffsets->getObject(partitionID); - partitionLengthNumber = - (OSNumber *)_nvramPartitionLengths->getObject(partitionID); - - if ((partitionOffsetNumber == 0) || (partitionLengthNumber == 0)) - return kIOReturnNotFound; - - partitionOffset = partitionOffsetNumber->unsigned32BitValue(); - partitionLength = partitionLengthNumber->unsigned32BitValue(); - - if ((buffer == 0) || (length == 0) || - (offset + length > partitionLength)) - return kIOReturnBadArgument; - - bcopy(buffer, _nvramImage + partitionOffset + offset, length); - - _nvramImageDirty = true; - - return kIOReturnSuccess; -} + registerService(); -IOByteCount IODTNVRAM::savePanicInfo(UInt8 *buffer, IOByteCount length) -{ - if ((_piImage == 0) || (length <= 0)) return 0; - - if (length > (_piPartitionSize - 4)) - length = _piPartitionSize - 4; - - // Save the Panic Info. - bcopy(buffer, _piImage + 4, length); - - // Save the Panic Info length. - *(UInt32 *)_piImage = length; - - _nvramImageDirty = true; - /* - * This prevents OF variables from being committed if the system has panicked - */ - _systemPaniced = true; - /* The call to sync() forces the NVRAM controller to write the panic info - * partition to NVRAM. - */ - sync(); - - return length; -} + return true; -// Private methods +error: + stop(provider); -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; + return false; } -struct OWVariablesHeader { - UInt16 owMagic; - UInt8 owVersion; - UInt8 owPages; - UInt16 owChecksum; - UInt16 owHere; - UInt16 owTop; - UInt16 owNext; - UInt32 owFlags; - UInt32 owNumbers[9]; - struct { - UInt16 offset; - UInt16 length; - } owStrings[10]; -}; -typedef struct OWVariablesHeader OWVariablesHeader; - -IOReturn IODTNVRAM::initOFVariables(void) -{ - UInt32 cnt, propOffset, propType; - UInt8 *propName, *propData; - UInt32 propNameLength, propDataLength; - const OSSymbol *propSymbol; - OSObject *propObject; - OWVariablesHeader *owHeader; - - if (_ofImage == 0) return kIOReturnNotReady; - - _ofDict = OSDictionary::withCapacity(1); - if (_ofDict == 0) return kIOReturnNoMemory; - - if (getPlatform()->getBootROMType()) { - cnt = 0; - while (cnt < _ofPartitionSize) { - // Break if there is no name. - if (_ofImage[cnt] == '\0') break; - - // Find the length of the name. - propName = _ofImage + cnt; - for (propNameLength = 0; (cnt + propNameLength) < _ofPartitionSize; - propNameLength++) { - if (_ofImage[cnt + propNameLength] == '=') break; - } - - // Break if the name goes past the end of the partition. - if ((cnt + propNameLength) >= _ofPartitionSize) break; - cnt += propNameLength + 1; - - propData = _ofImage + cnt; - for (propDataLength = 0; (cnt + propDataLength) < _ofPartitionSize; - propDataLength++) { - if (_ofImage[cnt + propDataLength] == '\0') break; - } - - // Break if the data goes past the end of the partition. - if ((cnt + propDataLength) >= _ofPartitionSize) break; - cnt += propDataLength + 1; - - if (convertPropToObject(propName, propNameLength, - propData, propDataLength, - &propSymbol, &propObject)) { - _ofDict->setObject(propSymbol, propObject); - propSymbol->release(); - propObject->release(); - } - } - - // Create the boot-args property if it is not in the dictionary. - if (_ofDict->getObject("boot-args") == 0) { - propObject = OSString::withCStringNoCopy(""); - if (propObject != 0) { - _ofDict->setObject("boot-args", propObject); - propObject->release(); - } - } - - // Create the 'aapl,panic-info' property if needed. - if (_piImage != 0) { - propDataLength = *(UInt32 *)_piImage; - if ((propDataLength != 0) && (propDataLength <= (_piPartitionSize - 4))) { - propObject = OSData::withBytes(_piImage + 4, propDataLength); - _ofDict->setObject(kIODTNVRAMPanicInfoKey, propObject); - propObject->release(); - - // Clear the length from _piImage and mark dirty. - *(UInt32 *)_piImage = 0; - _nvramImageDirty = true; - } - } - } else { - owHeader = (OWVariablesHeader *)_ofImage; - if (!validateOWChecksum(_ofImage)) { - _ofDict->release(); - _ofDict = 0; - return kIOReturnBadMedia; - } - - cnt = 0; - while (1) { - if (!getOWVariableInfo(cnt++, &propSymbol, &propType, &propOffset)) - break; - - switch (propType) { - case kOFVariableTypeBoolean : - propObject = OSBoolean::withBoolean(owHeader->owFlags & propOffset); - break; - - case kOFVariableTypeNumber : - propObject = OSNumber::withNumber(owHeader->owNumbers[propOffset], 32); - break; - - case kOFVariableTypeString : - propData = _ofImage + owHeader->owStrings[propOffset].offset - - _ofPartitionOffset; - propDataLength = owHeader->owStrings[propOffset].length; - propName = IONew(UInt8, propDataLength + 1); - if (propName != 0) { - strncpy((char *)propName, (const char *)propData, propDataLength); - propName[propDataLength] = '\0'; - propObject = OSString::withCString((const char *)propName); - IODelete(propName, UInt8, propDataLength + 1); +IOReturn +IODTNVRAMVariables::setProperties(OSObject * properties) +{ + if (OSDynamicCast(OSDictionary, properties)) { + OSSafeReleaseNULL(_properties); + _properties = OSDynamicCast(OSDictionary, properties); + properties->retain(); } - break; - } - - if (propObject == 0) break; - - _ofDict->setObject(propSymbol, propObject); - propSymbol->release(); - propObject->release(); - } - - // Create the boot-args property. - propSymbol = OSSymbol::withCString("boot-command"); - if (propSymbol != 0) { - propObject = _ofDict->getObject(propSymbol); - if (propObject != 0) { - updateOWBootArgs(propSymbol, propObject); - } - propSymbol->release(); - } - } - - return kIOReturnSuccess; -} -IOReturn IODTNVRAM::syncOFVariables(void) -{ - bool ok; - UInt32 cnt, length, maxLength; - UInt32 curOffset, tmpOffset, tmpType, tmpDataLength; - UInt8 *buffer, *tmpBuffer; - const UInt8 *tmpData; - const OSSymbol *tmpSymbol; - OSObject *tmpObject; - OSBoolean *tmpBoolean; - OSNumber *tmpNumber; - OSString *tmpString; - OSCollectionIterator *iter; - OWVariablesHeader *owHeader, *owHeaderOld; - - if ((_ofImage == 0) || (_ofDict == 0)) return kIOReturnNotReady; - - if (!_ofImageDirty) return kIOReturnSuccess; - - if (getPlatform()->getBootROMType()) { - buffer = tmpBuffer = IONew(UInt8, _ofPartitionSize); - if (buffer == 0) return kIOReturnNoMemory; - bzero(buffer, _ofPartitionSize); - - ok = true; - maxLength = _ofPartitionSize; - - 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) { - tmpBuffer += length; - maxLength -= length; - } - } - iter->release(); - - if (ok) { - bcopy(buffer, _ofImage, _ofPartitionSize); - } - - IODelete(buffer, UInt8, _ofPartitionSize); - - if (!ok) return kIOReturnBadArgument; - } else { - buffer = IONew(UInt8, _ofPartitionSize); - if (buffer == 0) return kIOReturnNoMemory; - bzero(buffer, _ofPartitionSize); - - owHeader = (OWVariablesHeader *)buffer; - owHeaderOld = (OWVariablesHeader *)_ofImage; - - owHeader->owMagic = owHeaderOld->owMagic; - owHeader->owVersion = owHeaderOld->owVersion; - owHeader->owPages = owHeaderOld->owPages; - - curOffset = _ofPartitionSize; - - ok = true; - cnt = 0; - while (ok) { - if (!getOWVariableInfo(cnt++, &tmpSymbol, &tmpType, &tmpOffset)) - break; - - tmpObject = _ofDict->getObject(tmpSymbol); - - switch (tmpType) { - case kOFVariableTypeBoolean : - tmpBoolean = OSDynamicCast(OSBoolean, tmpObject); - if (tmpBoolean->getValue()) owHeader->owFlags |= tmpOffset; - break; - - case kOFVariableTypeNumber : - tmpNumber = OSDynamicCast(OSNumber, tmpObject); - owHeader->owNumbers[tmpOffset] = tmpNumber->unsigned32BitValue(); - break; - - case kOFVariableTypeString : - tmpString = OSDynamicCast(OSString, tmpObject); - tmpData = (const UInt8 *)tmpString->getCStringNoCopy(); - tmpDataLength = tmpString->getLength(); - - if ((curOffset - tmpDataLength) < sizeof(OWVariablesHeader)) { - ok = false; - break; - } - - owHeader->owStrings[tmpOffset].length = tmpDataLength; - curOffset -= tmpDataLength; - owHeader->owStrings[tmpOffset].offset = curOffset + _ofPartitionOffset; - if (tmpDataLength != 0) - bcopy(tmpData, buffer + curOffset, tmpDataLength); - break; - } - } - - if (ok) { - owHeader->owHere = _ofPartitionOffset + sizeof(OWVariablesHeader); - owHeader->owTop = _ofPartitionOffset + curOffset; - owHeader->owNext = 0; - - owHeader->owChecksum = 0; - owHeader->owChecksum = ~generateOWChecksum(buffer); - - bcopy(buffer, _ofImage, _ofPartitionSize); - } - - IODelete(buffer, UInt8, _ofPartitionSize); - - if (!ok) return kIOReturnBadArgument; - } - - _ofImageDirty = false; - _nvramImageDirty = true; - - return kIOReturnSuccess; + return IOService::setProperties(properties); } -struct OFVariable { - const char *variableName; - UInt32 variableType; - UInt32 variablePerm; - SInt32 variableOffset; -}; -typedef struct OFVariable OFVariable; +bool +IODTNVRAMVariables::serializeProperties(OSSerialize *s) const +{ + const OSSymbol *key; + OSSharedPtr dict; + OSSharedPtr iter; + OSSharedPtr localProperties(_properties, OSRetain); + bool result = false; -enum { - kOWVariableOffsetNumber = 8, - kOWVariableOffsetString = 17 -}; + require(localProperties != nullptr, exit); -OFVariable gOFVariables[] = { - {"little-endian?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 0}, - {"real-mode?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 1}, - {"auto-boot?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 2}, - {"diag-switch?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 3}, - {"fcode-debug?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 4}, - {"oem-banner?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 5}, - {"oem-logo?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 6}, - {"use-nvramrc?", kOFVariableTypeBoolean, kOFVariablePermUserRead, 7}, - {"use-generic?", kOFVariableTypeBoolean, kOFVariablePermUserRead, -1}, - {"default-mac-address?", kOFVariableTypeBoolean, kOFVariablePermUserRead,-1}, - {"real-base", kOFVariableTypeNumber, kOFVariablePermUserRead, 8}, - {"real-size", kOFVariableTypeNumber, kOFVariablePermUserRead, 9}, - {"virt-base", kOFVariableTypeNumber, kOFVariablePermUserRead, 10}, - {"virt-size", kOFVariableTypeNumber, kOFVariablePermUserRead, 11}, - {"load-base", kOFVariableTypeNumber, kOFVariablePermUserRead, 12}, - {"pci-probe-list", kOFVariableTypeNumber, kOFVariablePermUserRead, 13}, - {"pci-probe-mask", kOFVariableTypeNumber, kOFVariablePermUserRead, -1}, - {"screen-#columns", kOFVariableTypeNumber, kOFVariablePermUserRead, 14}, - {"screen-#rows", kOFVariableTypeNumber, kOFVariablePermUserRead, 15}, - {"selftest-#megs", kOFVariableTypeNumber, kOFVariablePermUserRead, 16}, - {"boot-device", kOFVariableTypeString, kOFVariablePermUserRead, 17}, - {"boot-file", kOFVariableTypeString, kOFVariablePermUserRead, 18}, - {"boot-screen", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"console-screen", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"diag-device", kOFVariableTypeString, kOFVariablePermUserRead, 19}, - {"diag-file", kOFVariableTypeString, kOFVariablePermUserRead, 20}, - {"input-device", kOFVariableTypeString, kOFVariablePermUserRead, 21}, - {"output-device", kOFVariableTypeString, kOFVariablePermUserRead, 22}, - {"input-device-1", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"output-device-1", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"mouse-device", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"oem-banner", kOFVariableTypeString, kOFVariablePermUserRead, 23}, - {"oem-logo", kOFVariableTypeString, kOFVariablePermUserRead, 24}, - {"nvramrc", kOFVariableTypeString, kOFVariablePermUserRead, 25}, - {"boot-command", kOFVariableTypeString, kOFVariablePermUserRead, 26}, - {"default-client-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"default-server-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"default-gateway-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"default-subnet-mask", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"default-router-ip", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"boot-script", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"boot-args", kOFVariableTypeString, kOFVariablePermUserRead, -1}, - {"aapl,pci", kOFVariableTypeData, kOFVariablePermRootOnly, -1}, - {"security-mode", kOFVariableTypeString, 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}, -#endif - {0, kOFVariableTypeData, kOFVariablePermUserRead, -1} -}; + dict = OSDictionary::withCapacity(localProperties->getCount()); + require_action(dict, exit, DEBUG_ERROR("No dictionary\n")); -UInt32 IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const -{ - OFVariable *ofVar; - - ofVar = gOFVariables; - while (1) { - if ((ofVar->variableName == 0) || - propSymbol->isEqualTo(ofVar->variableName)) break; - ofVar++; - } - - return ofVar->variableType; -} + iter = OSCollectionIterator::withCollection(localProperties.get()); + require_action(iter, exit, DEBUG_ERROR("failed to create iterator\n")); + + while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) { + if (verifyPermission(kIONVRAMOperationRead, &_guid, key)) { + dict->setObject(key, localProperties->getObject(key)); + } + } -UInt32 IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const -{ - OFVariable *ofVar; - - ofVar = gOFVariables; - while (1) { - if ((ofVar->variableName == 0) || - propSymbol->isEqualTo(ofVar->variableName)) break; - ofVar++; - } - - return ofVar->variablePerm; + result = dict->serialize(s); + +exit: + DEBUG_INFO("result=%d\n", result); + return result; } -bool IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol, - UInt32 *propType, UInt32 *propOffset) -{ - OFVariable *ofVar; - - ofVar = gOFVariables; - while (1) { - if (ofVar->variableName == 0) return false; - - if (ofVar->variableOffset == (SInt32) variableNumber) break; - - ofVar++; - } - - *propSymbol = OSSymbol::withCStringNoCopy(ofVar->variableName); - *propType = ofVar->variableType; - - switch (*propType) { - case kOFVariableTypeBoolean : - *propOffset = 1 << (31 - variableNumber); - break; - - case kOFVariableTypeNumber : - *propOffset = variableNumber - kOWVariableOffsetNumber; - break; - - case kOFVariableTypeString : - *propOffset = variableNumber - kOWVariableOffsetString; - break; - } - - return true; +bool +IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane) +{ + OSSharedPtr dict; + + if (!super::init(old, plane)) { + return false; + } + + _variableLock = IOLockAlloc(); + if (!_variableLock) { + return false; + } + + _controllerLock = IOLockAlloc(); + if (!_controllerLock) { + return false; + } + + PE_parse_boot_argn("nvram-log", &gNVRAMLogging, sizeof(gNVRAMLogging)); + + dict = OSDictionary::withCapacity(1); + if (dict == nullptr) { + return false; + } + setPropertyTable(dict.get()); + dict.reset(); + + _nvramSize = getNVRAMSize(); + if (_nvramSize == 0) { + DEBUG_ERROR("NVRAM : Error - default size not specified in DT\n"); + return false; + } + // partition offsets are UInt16 (bytes / 0x10) + 1 + if (_nvramSize > 0xFFFF * 0x10) { + DEBUG_ERROR("NVRAM : truncating _nvramSize from %ld\n", (long) _nvramSize); + _nvramSize = 0xFFFF * 0x10; + } + _nvramImage = IONew(UInt8, _nvramSize); + if (_nvramImage == nullptr) { + return false; + } + + _nvramPartitionOffsets = OSDictionary::withCapacity(1); + if (_nvramPartitionOffsets == nullptr) { + return false; + } + + _nvramPartitionLengths = OSDictionary::withCapacity(1); + if (_nvramPartitionLengths == nullptr) { + return false; + } + + _registryPropertiesKey = OSSymbol::withCStringNoCopy("aapl,pci"); + if (_registryPropertiesKey == nullptr) { + return false; + } + + // race condition possible between + // IODTNVRAM and IONVRAMController (restore loses boot-args) + initProxyData(); + + // Require at least the common partition to be present and error free + if (_commonDict == nullptr) { + return false; + } + + return true; } -bool IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength, - UInt8 *propData, UInt32 propDataLength, - const OSSymbol **propSymbol, - OSObject **propObject) -{ - UInt32 propType; - const OSSymbol *tmpSymbol; - OSObject *tmpObject; - OSNumber *tmpNumber; - OSString *tmpString; - - // Create the symbol. - propName[propNameLength] = '\0'; - tmpSymbol = OSSymbol::withCString((const char *)propName); - propName[propNameLength] = '='; - if (tmpSymbol == 0) { - return false; - } - - propType = getOFVariableType(tmpSymbol); - - // Create the object. - tmpObject = 0; - switch (propType) { - case kOFVariableTypeBoolean : - if (!strncmp("true", (const char *)propData, propDataLength)) { - tmpObject = kOSBooleanTrue; - } else if (!strncmp("false", (const char *)propData, propDataLength)) { - tmpObject = kOSBooleanFalse; - } - break; - - case kOFVariableTypeNumber : - tmpNumber = OSNumber::withNumber(strtol((const char *)propData, 0, 0), 32); - if (tmpNumber != 0) tmpObject = tmpNumber; - break; - - case kOFVariableTypeString : - tmpString = OSString::withCString((const char *)propData); - if (tmpString != 0) tmpObject = tmpString; - break; - - case kOFVariableTypeData : - tmpObject = unescapeBytesToData(propData, propDataLength); - break; - } - - if (tmpObject == 0) { - tmpSymbol->release(); - return false; - } - - *propSymbol = tmpSymbol; - *propObject = tmpObject; - - return true; +void +IODTNVRAM::initProxyData(void) +{ + OSSharedPtr entry; + const char *key = "nvram-proxy-data"; + OSData *data; + const void *bytes; + + entry = IORegistryEntry::fromPath("/chosen", gIODTPlane); + if (entry != nullptr) { + OSSharedPtr prop = entry->copyProperty(key); + if (prop != nullptr) { + data = OSDynamicCast(OSData, prop.get()); + if (data != nullptr) { + bytes = data->getBytesNoCopy(); + if ((bytes != nullptr) && (data->getLength() <= _nvramSize)) { + bcopy(bytes, _nvramImage, data->getLength()); + initNVRAMImage(); + _isProxied = true; + } + } + } + entry->removeProperty(key); + } } -bool IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length, - const OSSymbol *propSymbol, OSObject *propObject) -{ - const UInt8 *propName; - UInt32 propNameLength, propDataLength; - UInt32 propType, tmpValue; - OSBoolean *tmpBoolean = 0; - OSNumber *tmpNumber = 0; - OSString *tmpString = 0; - OSData *tmpData = 0; - - propName = (const UInt8 *)propSymbol->getCStringNoCopy(); - propNameLength = propSymbol->getLength(); - propType = getOFVariableType(propSymbol); - - // Get the size of the data. - propDataLength = 0xFFFFFFFF; - switch (propType) { - case kOFVariableTypeBoolean : - tmpBoolean = OSDynamicCast(OSBoolean, propObject); - if (tmpBoolean != 0) propDataLength = 5; - break; - - case kOFVariableTypeNumber : - tmpNumber = OSDynamicCast(OSNumber, propObject); - if (tmpNumber != 0) propDataLength = 10; - break; - - case kOFVariableTypeString : - tmpString = OSDynamicCast(OSString, propObject); - if (tmpString != 0) propDataLength = tmpString->getLength(); - break; - - case kOFVariableTypeData : - tmpData = OSDynamicCast(OSData, propObject); - if (tmpData != 0) { - tmpData = escapeDataToData(tmpData); - propDataLength = tmpData->getLength(); - } - 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); - - switch (propType) { - case kOFVariableTypeBoolean : - if (tmpBoolean->getValue()) { - strlcpy((char *)buffer, "true", *length - propNameLength); - } else { - strlcpy((char *)buffer, "false", *length - propNameLength); - } - break; - - case kOFVariableTypeNumber : - tmpValue = tmpNumber->unsigned32BitValue(); - if (tmpValue == 0xFFFFFFFF) { - strlcpy((char *)buffer, "-1", *length - propNameLength); - } else if (tmpValue < 1000) { - snprintf((char *)buffer, *length - propNameLength, "%d", (uint32_t)tmpValue); - } else { - snprintf((char *)buffer, *length - propNameLength, "0x%x", (uint32_t)tmpValue); - } - break; - - case kOFVariableTypeString : - strlcpy((char *)buffer, tmpString->getCStringNoCopy(), *length - propNameLength); - break; - - case kOFVariableTypeData : - bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength); - tmpData->release(); - break; - } - - propDataLength = strlen((const char *)buffer); - - *length = propNameLength + propDataLength + 2; - - return true; +UInt32 +IODTNVRAM::getNVRAMSize(void) +{ + OSSharedPtr entry; + const char *key = "nvram-total-size"; + OSData *data; + UInt32 size = 0; + + entry = IORegistryEntry::fromPath("/chosen", gIODTPlane); + if (entry != nullptr) { + OSSharedPtr prop = entry->copyProperty(key); + if (prop != nullptr) { + data = OSDynamicCast(OSData, prop.get()); + if (data != nullptr) { + size = *((UInt32*)data->getBytesNoCopy()); + DEBUG_ALWAYS("NVRAM size is %u bytes\n", (unsigned int) size); + } + } + } + return size; } -UInt16 IODTNVRAM::generateOWChecksum(UInt8 *buffer) +void +IODTNVRAM::registerNVRAMController(IONVRAMController *nvram) { - UInt32 cnt, checksum = 0; - UInt16 *tmpBuffer = (UInt16 *)buffer; - - for (cnt = 0; cnt < _ofPartitionSize / 2; cnt++) - checksum += tmpBuffer[cnt]; - - return checksum % 0x0000FFFF; -} + IOReturn ret; -bool IODTNVRAM::validateOWChecksum(UInt8 *buffer) -{ - UInt32 cnt, checksum, sum = 0; - UInt16 *tmpBuffer = (UInt16 *)buffer; - - for (cnt = 0; cnt < _ofPartitionSize / 2; cnt++) - sum += tmpBuffer[cnt]; - - checksum = (sum >> 16) + (sum & 0x0000FFFF); - if (checksum == 0x10000) checksum--; - checksum = (checksum ^ 0x0000FFFF) & 0x0000FFFF; - - return checksum == 0; -} + if (_nvramController != nullptr) { + DEBUG_ERROR("Duplicate controller set\n"); + return; + } -void IODTNVRAM::updateOWBootArgs(const OSSymbol *key, OSObject *value) -{ - bool wasBootArgs, bootr = false; - UInt32 cnt; - OSString *tmpString, *bootCommand, *bootArgs = 0; - const UInt8 *bootCommandData, *bootArgsData; - UInt8 *tmpData; - UInt32 bootCommandDataLength, bootArgsDataLength, tmpDataLength; - - tmpString = OSDynamicCast(OSString, value); - if (tmpString == 0) return; - - if (key->isEqualTo("boot-command")) { - wasBootArgs = false; - bootCommand = tmpString; - } else if (key->isEqualTo("boot-args")) { - wasBootArgs = true; - bootArgs = tmpString; - bootCommand = OSDynamicCast(OSString, _ofDict->getObject("boot-command")); - if (bootCommand == 0) return; - } else return; - - bootCommandData = (const UInt8 *)bootCommand->getCStringNoCopy(); - bootCommandDataLength = bootCommand->getLength(); - - if (bootCommandData == 0) return; - - for (cnt = 0; cnt < bootCommandDataLength; cnt++) { - if ((bootCommandData[cnt] == 'b') && - !strncmp("bootr", (const char *)bootCommandData + cnt, 5)) { - cnt += 5; - while (bootCommandData[cnt] == ' ') cnt++; - bootr = true; - break; - } - } - if (!bootr) { - _ofDict->removeObject("boot-args"); - return; - } - - if (wasBootArgs) { - bootArgsData = (const UInt8 *)bootArgs->getCStringNoCopy(); - bootArgsDataLength = bootArgs->getLength(); - if (bootArgsData == 0) return; - - tmpDataLength = cnt + bootArgsDataLength; - tmpData = IONew(UInt8, tmpDataLength + 1); - if (tmpData == 0) return; - - cnt -= strlcpy((char *)tmpData, (const char *)bootCommandData, cnt); - strlcat((char *)tmpData, (const char *)bootArgsData, cnt); - - bootCommand = OSString::withCString((const char *)tmpData); - if (bootCommand != 0) { - _ofDict->setObject("boot-command", bootCommand); - bootCommand->release(); - } - - IODelete(tmpData, UInt8, tmpDataLength + 1); - } else { - bootArgs = OSString::withCString((const char *)(bootCommandData + cnt)); - if (bootArgs != 0) { - _ofDict->setObject("boot-args", bootArgs); - bootArgs->release(); - } - } -} + DEBUG_INFO("setting controller\n"); + _nvramController = nvram; -// Private methods for Name Registry access. + // race condition possible between + // IODTNVRAM and IONVRAMController (restore loses boot-args) + if (!_isProxied) { + DEBUG_INFO("Proxied NVRAM data\n"); + _nvramController->read(0, _nvramImage, _nvramSize); + initNVRAMImage(); + } -enum { - kMaxNVNameLength = 4, - kMaxNVDataLength = 8 -}; + if (_systemPartitionSize) { + _systemService = new IODTNVRAMVariables; -struct NVRAMProperty -{ - IONVRAMDescriptor header; - UInt8 nameLength; - UInt8 name[ kMaxNVNameLength ]; - UInt8 dataLength; - UInt8 data[ kMaxNVDataLength ]; -}; + if (!_systemService || !_systemService->init(&gAppleSystemVariableGuid)) { + DEBUG_ERROR("Unable to start the system service!\n"); + goto no_system; + } -bool IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor *hdr, UInt32 *where) -{ - UInt32 offset; - SInt32 nvEnd; - - nvEnd = *((UInt16 *)_nrImage); - if(getPlatform()->getBootROMType()) { - // on NewWorld, offset to partition start - nvEnd -= 0x100; - } else { - // on old world, absolute - nvEnd -= _nrPartitionOffset; - } - if((nvEnd < 0) || (nvEnd >= kIODTNVRAMNameRegistrySize)) - nvEnd = 2; - - offset = 2; - while ((offset + sizeof(NVRAMProperty)) <= (UInt32)nvEnd) { - if (bcmp(_nrImage + offset, hdr, sizeof(*hdr)) == 0) { - *where = offset; - return true; - } - offset += sizeof(NVRAMProperty); - } - - if ((nvEnd + sizeof(NVRAMProperty)) <= kIODTNVRAMNameRegistrySize) - *where = nvEnd; - else - *where = 0; - - return false; -} + _systemService->setName("options-system"); + + if (!_systemService->attach(this)) { + DEBUG_ERROR("Unable to attach the system service!\n"); + OSSafeReleaseNULL(_systemService); + goto no_system; + } + + if (!_systemService->start(this)) { + DEBUG_ERROR("Unable to start the system service!\n"); + _systemService->detach(this); + OSSafeReleaseNULL(_systemService); + goto no_system; + } + } + +no_system: + if (_commonPartitionSize) { + _commonService = new IODTNVRAMVariables; + + if (!_commonService || !_commonService->init(&gAppleNVRAMGuid)) { + DEBUG_ERROR("Unable to start the common service!\n"); + goto no_common; + } + + _commonService->setName("options-common"); + + if (!_commonService->attach(this)) { + DEBUG_ERROR("Unable to attach the common service!\n"); + OSSafeReleaseNULL(_commonService); + goto no_common; + } + + if (!_commonService->start(this)) { + DEBUG_ERROR("Unable to start the common service!\n"); + _commonService->detach(this); + OSSafeReleaseNULL(_commonService); + goto no_common; + } + } -IOReturn IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry *entry, - const OSSymbol **name, - OSData **value) -{ - IONVRAMDescriptor hdr; - NVRAMProperty *prop; - IOByteCount length; - UInt32 offset; - IOReturn err; - char nameBuf[kMaxNVNameLength + 1]; - - if (_nrImage == 0) return kIOReturnUnsupported; - if ((entry == 0) || (name == 0) || (value == 0)) return kIOReturnBadArgument; - - err = IODTMakeNVDescriptor(entry, &hdr); - if (err != kIOReturnSuccess) return err; - - if (searchNVRAMProperty(&hdr, &offset)) { - prop = (NVRAMProperty *)(_nrImage + offset); - - length = prop->nameLength; - if (length > kMaxNVNameLength) length = kMaxNVNameLength; - strncpy(nameBuf, (const char *)prop->name, length); - nameBuf[length] = 0; - *name = OSSymbol::withCString(nameBuf); - - length = prop->dataLength; - if (length > kMaxNVDataLength) length = kMaxNVDataLength; - *value = OSData::withBytes(prop->data, length); - - if ((*name != 0) && (*value != 0)) return kIOReturnSuccess; - else return kIOReturnNoMemory; - } - - return kIOReturnNoResources; +no_common: + ret = serializeVariables(); + DEBUG_INFO("serializeVariables ret=0x%08x\n", ret); } -IOReturn IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry *entry, - const OSSymbol *name, - OSData *value) -{ - IONVRAMDescriptor hdr; - NVRAMProperty *prop; - IOByteCount nameLength; - IOByteCount dataLength; - UInt32 offset; - IOReturn err; - UInt16 nvLength; - bool exists; - - if (_nrImage == 0) return kIOReturnUnsupported; - if ((entry == 0) || (name == 0) || (value == 0)) return kIOReturnBadArgument; - - nameLength = name->getLength(); - dataLength = value->getLength(); - if (nameLength > kMaxNVNameLength) return kIOReturnNoSpace; - if (dataLength > kMaxNVDataLength) return kIOReturnNoSpace; - - err = IODTMakeNVDescriptor(entry, &hdr); - if (err != kIOReturnSuccess) return err; - - exists = searchNVRAMProperty(&hdr, &offset); - if (offset == 0) return kIOReturnNoMemory; - - prop = (NVRAMProperty *)(_nrImage + offset); - if (!exists) bcopy(&hdr, &prop->header, sizeof(hdr)); - - prop->nameLength = nameLength; - bcopy(name->getCStringNoCopy(), prop->name, nameLength); - prop->dataLength = dataLength; - bcopy(value->getBytesNoCopy(), prop->data, dataLength); - - if (!exists) { - nvLength = offset + sizeof(NVRAMProperty); - if (getPlatform()->getBootROMType()) - nvLength += 0x100; - else - nvLength += _nrPartitionOffset; - *((UInt16 *)_nrImage) = nvLength; - } - - _nvramImageDirty = true; - - return err; +void +IODTNVRAM::initNVRAMImage(void) +{ + char partitionID[18]; + UInt32 partitionOffset, partitionLength; + UInt32 currentLength, currentOffset = 0; + + _commonPartitionOffset = 0xFFFFFFFF; + _systemPartitionOffset = 0xFFFFFFFF; + + // Look through the partitions to find the OF and System partitions. + while (currentOffset < _nvramSize) { + bool common_partition; + bool system_partition; + + chrp_nvram_header_t * header = (chrp_nvram_header_t *)(_nvramImage + currentOffset); + + 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) > _nvramSize) { + break; + } + + common_partition = memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_COMMON, strlen(NVRAM_CHRP_PARTITION_NAME_COMMON)) == 0; + system_partition = (memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM)) == 0) || + (memcmp(header->name, NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY, strlen(NVRAM_CHRP_PARTITION_NAME_SYSTEM_LEGACY)) == 0); + + if (common_partition) { + _commonPartitionOffset = partitionOffset; + _commonPartitionSize = partitionLength; + } else if (system_partition) { + _systemPartitionOffset = partitionOffset; + _systemPartitionSize = partitionLength; + } else { + OSSharedPtr partitionOffsetNumber, partitionLengthNumber; + + // Construct the partition ID from the signature and name. + snprintf(partitionID, sizeof(partitionID), "0x%02x,", header->sig); + strncpy(partitionID + 5, header->name, sizeof(header->name)); + partitionID[17] = '\0'; + + partitionOffsetNumber = OSNumber::withNumber(partitionOffset, 32); + partitionLengthNumber = OSNumber::withNumber(partitionLength, 32); + + // Save the partition offset and length + _nvramPartitionOffsets->setObject(partitionID, partitionOffsetNumber.get()); + _nvramPartitionLengths->setObject(partitionID, partitionLengthNumber.get()); + } + currentOffset += currentLength; + } + + if (_commonPartitionOffset != 0xFFFFFFFF) { + _commonImage = _nvramImage + _commonPartitionOffset; + } + + if (_systemPartitionOffset != 0xFFFFFFFF) { + _systemImage = _nvramImage + _systemPartitionOffset; + } + + DEBUG_ALWAYS("NVRAM : ofPartitionOffset - 0x%x, ofPartitionSize - 0x%x, systemPartitionOffset - 0x%x, systemPartitionSize - 0x%x\n", + (unsigned int) _commonPartitionOffset, (unsigned int) _commonPartitionSize, (unsigned int) _systemPartitionOffset, (unsigned int) _systemPartitionSize); + + _lastDeviceSync = 0; + _freshInterval = TRUE; // we will allow sync() even before the first 15 minutes have passed. + + initVariables(); } -OSData *IODTNVRAM::unescapeBytesToData(const UInt8 *bytes, UInt32 length) -{ - OSData *data = 0; - 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 != 0) { - 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; +void +IODTNVRAM::syncInternal(bool rateLimit) +{ + DEBUG_INFO("rateLimit=%d\n", rateLimit); + + // Don't try to perform controller operations if none has been registered. + if (_nvramController == nullptr) { + 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; + } + + DEBUG_INFO("Calling sync()\n"); + + CONTROLLERLOCK(); + _nvramController->sync(); + CONTROLLERUNLOCK(); } -OSData * IODTNVRAM::escapeDataToData(OSData * value) -{ - 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(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) | (wherePtr - startPtr); - } - ok &= result->appendByte(byte, 1); - } - ok &= result->appendByte(0, 1); - - if (!ok) { - result->release(); - result = 0; - } - - return result; +void +IODTNVRAM::sync(void) +{ + syncInternal(false); } -static bool IsApplePropertyName(const char * propName) +bool +IODTNVRAM::serializeProperties(OSSerialize *s) const { - char c; - while ((c = *propName++)) { - if ((c >= 'A') && (c <= 'Z')) - break; - } + const OSSymbol *key; + OSSharedPtr systemDict, commonDict, dict; + OSSharedPtr iter; + bool result = false; + unsigned int totalCapacity = 0; + + NVRAMLOCK(); + if (_commonDict) { + commonDict = OSDictionary::withDictionary(_commonDict.get()); + } + + if (_systemDict) { + systemDict = OSDictionary::withDictionary(_systemDict.get()); + } + NVRAMUNLOCK(); + + totalCapacity += (commonDict != nullptr) ? commonDict->getCapacity() : 0; + totalCapacity += (systemDict != nullptr) ? systemDict->getCapacity() : 0; + + dict = OSDictionary::withCapacity(totalCapacity); - return (c == 0); + if (dict == nullptr) { + DEBUG_ERROR("No dictionary\n"); + goto exit; + } + + // Copy system entries first if present then copy unique common entries + if (systemDict != nullptr) { + iter = OSCollectionIterator::withCollection(systemDict.get()); + if (iter == nullptr) { + DEBUG_ERROR("failed to create iterator\n"); + goto exit; + } + + while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) { + if (verifyPermission(kIONVRAMOperationRead, &gAppleSystemVariableGuid, key)) { + dict->setObject(key, systemDict->getObject(key)); + } + } + + iter.reset(); + } + + if (commonDict != nullptr) { + iter = OSCollectionIterator::withCollection(commonDict.get()); + if (iter == nullptr) { + DEBUG_ERROR("failed to create common iterator\n"); + goto exit; + } + + while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) { + if (dict->getObject(key) != nullptr) { + // Skip non uniques + continue; + } + if (verifyPermission(kIONVRAMOperationRead, &gAppleNVRAMGuid, key)) { + dict->setObject(key, commonDict->getObject(key)); + } + } + } + + result = dict->serialize(s); + +exit: + DEBUG_INFO("result=%d\n", result); + + return result; } -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 = 0; - const char *nvName = 0; - const char *resultName = 0; - const UInt8 *resultValue = 0; - UInt32 resultValueLen = 0; - UInt8 byte; - - if (_ofDict == 0) return err; - data = OSDynamicCast(OSData, _ofDict->getObject(_registryPropertiesKey)); - if (data == 0) return err; - - startPtr = (const UInt8 *) data->getBytesNoCopy(); - endPtr = startPtr + data->getLength(); - - wherePtr = startPtr; - while (wherePtr < endPtr) { - byte = *(wherePtr++); - if (byte) - continue; - - if (nvPath == 0) - nvPath = startPtr; - else if (nvName == 0) - nvName = (const char *) startPtr; - else { - IORegistryEntry * compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane); - if (compareEntry) - compareEntry->release(); - if (entry == compareEntry) { - bool appleProp = IsApplePropertyName(nvName); - if (!appleProp || !resultName) { - resultName = nvName; - resultValue = startPtr; - resultValueLen = wherePtr - startPtr - 1; - } - if (!appleProp) - break; - } - nvPath = 0; - nvName = 0; - } - startPtr = wherePtr; - } - if (resultName) { - *name = OSSymbol::withCString(resultName); - *value = unescapeBytesToData(resultValue, resultValueLen); - if ((*name != 0) && (*value != 0)) - err = kIOReturnSuccess; - else - err = kIOReturnNoMemory; - } - return err; +IOReturn +IODTNVRAM::chooseDictionary(IONVRAMOperation operation, const uuid_t *varGuid, const char *variableName, OSDictionary **dict) const +{ + if (_systemDict != nullptr) { + bool systemGuid = uuid_compare(*varGuid, gAppleSystemVariableGuid) == 0; + + if (variableInAllowList(variableName)) { + DEBUG_INFO("Using system dictionary due to allow list\n"); + if (!systemGuid) { + DEBUG_ERROR("System GUID NOT used for %s\n", variableName); + } + *dict = _systemDict.get(); + } else if (systemGuid) { + DEBUG_INFO("Using system dictionary via GUID\n"); + *dict = _systemDict.get(); + } else { + DEBUG_INFO("Using common dictionary\n"); + *dict = _commonDict.get(); + } + } else { + DEBUG_INFO("Defaulting to common dictionary\n"); + *dict = _commonDict.get(); + } + + return kIOReturnSuccess; } -IOReturn IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry, - const OSSymbol *propName, - OSData *value) -{ - OSData *oldData; - OSData *data = 0; - const UInt8 *startPtr; - const UInt8 *propStart; - const UInt8 *endPtr; - const UInt8 *wherePtr; - const UInt8 *nvPath = 0; - const char *nvName = 0; - const char * comp; - const char * name; - UInt8 byte; - bool ok = true; - bool settingAppleProp; - - if (_ofDict == 0) return kIOReturnNoResources; - - settingAppleProp = IsApplePropertyName(propName->getCStringNoCopy()); - - // copy over existing properties for other entries - - oldData = OSDynamicCast(OSData, _ofDict->getObject(_registryPropertiesKey)); - 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 == 0) - nvPath = startPtr; - else if (nvName == 0) - nvName = (const char *) startPtr; - else { - IORegistryEntry * compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane); - if (compareEntry) - compareEntry->release(); - if (entry == compareEntry) { - if ((settingAppleProp && propName->isEqualTo(nvName)) - || (!settingAppleProp && !IsApplePropertyName(nvName))) { - // delete old property (nvPath -> wherePtr) - data = OSData::withBytes(propStart, nvPath - propStart); - if (data) - ok &= data->appendBytes(wherePtr, endPtr - wherePtr); - break; - } - } - nvPath = 0; - nvName = 0; - } - - startPtr = wherePtr; - } - } - - // make the new property - - if (!data) { - if (oldData) - data = OSData::withData(oldData); - else - data = OSData::withCapacity(16); - if (!data) - return kIOReturnNoMemory; - } - - if (value && value->getLength()) { - // get entries in path - OSArray *array = OSArray::withCapacity(5); - if (!array) { - data->release(); - return kIOReturnNoMemory; +bool +IODTNVRAM::handleSpecialVariables(const char *name, uuid_t *guid, OSObject *obj, IOReturn *error) +{ + IOReturn err = kIOReturnSuccess; + bool special = false; + + NVRAMLOCKASSERT(); + + if (strcmp(name, "ResetNVRam") == 0) { + DEBUG_INFO("%s requested\n", name); + + if (uuid_compare(*guid, gAppleSystemVariableGuid) == 0) { + if (_systemDict != nullptr) { + _systemDict->flushCollection(); + } + + _commonDict->flushCollection(); + DEBUG_INFO("system & common dictionary flushed\n"); } - 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 && (0 == strncmp("pci", name, sizeof("pci"))) - && (0 == strncmp("80000000", comp, sizeof("80000000")))) { - // yosemite hack - comp = "/pci@80000000"; - } else { - if (comp) - ok &= data->appendBytes("/@", 2); - else { - if (!name) - continue; - ok &= data->appendByte('/', 1); - comp = name; + + special = true; + } else if (strcmp(name, "ObliterateNVRam") == 0) { + DEBUG_INFO("%s requested\n", name); + + if ((_systemDict != nullptr) && (uuid_compare(*guid, gAppleSystemVariableGuid) == 0)) { + const OSSymbol *key; + OSSharedPtr newDict; + OSSharedPtr iter; + + newDict = OSDictionary::withCapacity(_systemDict->getCapacity()); + iter = OSCollectionIterator::withCollection(newDict.get()); + if ((newDict == nullptr) || (iter == nullptr)) { + err = kIOReturnNoMemory; + goto exit; + } + + while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) { + const OSSymbol *key = OSDynamicCast(OSSymbol, iter->getNextObject()); + if (key == nullptr) { + err = kIOReturnNoMemory; + goto exit; + } + + if (!verifyPermission(kIONVRAMOperationObliterate, &gAppleSystemVariableGuid, key)) { + newDict->setObject(key, _systemDict->getObject(key)); + } + } + + _systemDict = newDict; + + DEBUG_INFO("system dictionary flushed\n"); + } else if (_commonDict != nullptr) { + const OSSymbol *key; + OSSharedPtr newDict; + OSSharedPtr iter; + + newDict = OSDictionary::withCapacity(_commonDict->getCapacity()); + iter = OSCollectionIterator::withCollection(newDict.get()); + if ((newDict == nullptr) || (iter == nullptr)) { + err = kIOReturnNoMemory; + goto exit; + } + + while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) { + if (!verifyPermission(kIONVRAMOperationObliterate, &gAppleNVRAMGuid, key)) { + newDict->setObject(key, _commonDict->getObject(key)); } } - ok &= data->appendBytes(comp, strlen(comp)); - } - ok &= data->appendByte(0, 1); - array->release(); - - // 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); - } - if (ok) { - ok = _ofDict->setObject(_registryPropertiesKey, data); - if (ok) - _ofImageDirty = true; - } - data->release(); - - return ok ? kIOReturnSuccess : kIOReturnNoMemory; + + _commonDict = newDict; + + DEBUG_INFO("common dictionary flushed\n"); + } + + special = true; + } + +exit: + if (error) { + *error = err; + } + + return special; } -bool IODTNVRAM::safeToSync(void) +OSSharedPtr +IODTNVRAM::copyProperty(const OSSymbol *aKey) const { - 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); + IOReturn result; + const char *variableName; + uuid_t varGuid; + OSDictionary *dict; + OSSharedPtr 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()); - if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) - { + 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 +IODTNVRAM::copyProperty(const char *aKey) const +{ + OSSharedPtr keySymbol; + OSSharedPtr 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 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 theObject = copyProperty(aKey); + + return theObject.get(); +} + +IOReturn +IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject) +{ + IOReturn result = kIOReturnSuccess; + bool remove = false; + OSString *tmpString = nullptr; + OSSharedPtr propObject, oldObject; + OSSharedPtr 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 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(sharedObject); + break; + + case kOFVariableTypeNumber: + propObject = OSDynamicPtrCast(sharedObject); + break; + + case kOFVariableTypeString: + propObject = OSDynamicPtrCast(sharedObject); + if (propObject != nullptr) { + propDataSize = (OSDynamicPtrCast(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(sharedObject); + if (propObject == nullptr) { + tmpString = OSDynamicCast(OSString, sharedObject.get()); + if (tmpString != nullptr) { + propObject = OSData::withBytes(tmpString->getCStringNoCopy(), + tmpString->getLength()); + } + } + + if (propObject != nullptr) { + propDataSize = (OSDynamicPtrCast(propObject))->getLength(); + } + +#if defined(XNU_TARGET_OS_OSX) + if ((propObject != nullptr) && ((OSDynamicPtrCast(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 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 propSymbol; + OSSharedPtr 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 iter; + OSSharedPtr sizeUsed; + UInt32 systemUsed = 0; + UInt32 commonUsed = 0; + OSSharedPtr 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 tmpSymbol; + OSSharedPtr tmpNumber; + OSSharedPtr tmpString; + OSSharedPtr 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& propSymbol, + OSSharedPtr& 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 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 +IODTNVRAM::unescapeBytesToData(const UInt8 *bytes, UInt32 length) +{ + OSSharedPtr 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 +IODTNVRAM::escapeDataToData(OSData * value) +{ + OSSharedPtr 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 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 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 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 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 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;