darling-xnu/iokit/Kernel/IONVRAM.cpp

2589 lines
67 KiB
C++
Raw Normal View History

2023-05-17 04:41:14 +00:00
/*
* 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
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#define IOKIT_ENABLE_SHARED_PTR
#include <AssertMacros.h>
#include <IOKit/IOLib.h>
#include <IOKit/IONVRAM.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IOUserClient.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOKitKeysPrivate.h>
#include <IOKit/IOBSD.h>
#include <kern/debug.h>
#include <pexpert/boot.h>
#include <pexpert/pexpert.h>
#include <sys/csr.h>
#define super IOService
#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_PARTITION_NAME_COMMON_V1 "common"
#define NVRAM_CHRP_PARTITION_NAME_SYSTEM_V1 "system"
#define NVRAM_CHRP_PARTITION_NAME_COMMON_V2 "2common"
#define NVRAM_CHRP_PARTITION_NAME_SYSTEM_V2 "2system"
#define NVRAM_CHRP_LENGTH_BLOCK_SIZE 0x10 // CHRP length field is in 16 byte blocks
typedef struct chrp_nvram_header { //16 bytes
uint8_t sig;
uint8_t cksum; // checksum on sig, len, and name
uint16_t len; // total length of the partition in 16 byte blocks starting with the signature
// and ending with the last byte of data area, ie len includes its own header size
char name[12];
uint8_t data[0];
} chrp_nvram_header_t;
typedef struct apple_nvram_header { // 16 + 16 bytes
struct chrp_nvram_header chrp;
uint32_t adler;
uint32_t generation;
uint8_t padding[8];
} apple_nvram_header_t;
#define kIONVRAMPrivilege kIOClientPrivilegeAdministrator
OSDefineMetaClassAndStructors(IODTNVRAM, IOService);
#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
#define DEBUG_ERROR DEBUG_ALWAYS
#define CONTROLLERLOCK() \
({ \
if (preemption_enabled() && !panic_active()) \
IOLockLock(_controllerLock); \
})
#define CONTROLLERUNLOCK() \
({ \
if (preemption_enabled() && !panic_active()) \
IOLockUnlock(_controllerLock); \
})
#define NVRAMREADLOCK() \
({ \
if (preemption_enabled() && !panic_active()) \
IORWLockRead(_variableLock); \
})
#define NVRAMWRITELOCK() \
({ \
if (preemption_enabled() && !panic_active()) \
IORWLockWrite(_variableLock); \
})
#define NVRAMUNLOCK() \
({ \
if (preemption_enabled() && !panic_active()) \
IORWLockUnlock(_variableLock); \
})
#define NVRAMLOCKASSERTHELD() \
({ \
if (preemption_enabled() && !panic_active()) \
IORWLockAssert(_variableLock, kIORWLockAssertHeld); \
})
#define NVRAMLOCKASSERTEXCLUSIVE() \
({ \
if (preemption_enabled() && !panic_active()) \
IORWLockAssert(_variableLock, kIORWLockAssertWrite); \
})
enum NVRAMPartitionType {
kIONVRAMPartitionSystem,
kIONVRAMPartitionCommon
};
typedef struct {
NVRAMPartitionType type;
UInt32 offset;
UInt32 size;
OSSharedPtr<OSDictionary> &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;
static bool gInternalBuild = false;
// allowlist variables from macboot that need to be set/get from system region if present
static const char * const gNVRAMSystemList[] = {
"allow-root-hash-mismatch",
"auto-boot",
"auto-boot-halt-stage",
"base-system-path",
"boot-args",
"boot-command",
"boot-image",
"bootdelay",
"com.apple.System.boot-nonce",
"darkboot",
"emu",
"one-time-boot-command", // Needed for diags customer install flows
"policy-nonce-digests",
"prevent-restores", // Keep for factory <rdar://problem/70476321>
"prev-lang:kbd",
"root-live-fs",
"StartupMute", // Set by customers via nvram tool
"SystemAudioVolume",
"SystemAudioVolumeExtension",
"SystemAudioVolumeSaved",
nullptr
};
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
};
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 SystemReadHidden :1;
uint64_t FullAccess :1;
uint64_t Reserved:57;
} 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}, // Deleting this via user triggered obliterate leave J273a unable to boot
{"recoveryos-passcode-blob", .p.Bits.SystemReadHidden = 1},
{"security-password", .p.Bits.RootRequired = 1},
{"system-passcode-lock-blob", .p.Bits.SystemReadHidden = 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},
{"backlight-nits", .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++;
}
return entry->type;
}
static IONVRAMVariableType
getVariableType(const OSSymbol *propSymbol)
{
return getVariableType(propSymbol->getCStringNoCopy());
}
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;
}
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;
}
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 {
return propDataSize <= 768;
}
}
return true;
}
#if defined(DEBUG) || defined(DEVELOPMENT)
static const char *
getNVRAMOpString(IONVRAMOperation op)
{
switch (op) {
case kIONVRAMOperationRead:
return "Read";
case kIONVRAMOperationWrite:
return "Write";
case kIONVRAMOperationDelete:
return "Delete";
case kIONVRAMOperationObliterate:
return "Obliterate";
case kIONVRAMOperationReset:
return "Reset";
default:
return "Unknown";
}
}
#endif
static bool
verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const char *varName)
{
VariablePermission perm;
bool kernel, writeEntitled, readEntitled, allowList, systemGuid, systemEntitled, systemInternalEntitled, systemAllow, systemReadHiddenAllow;
bool admin = false;
bool ok = false;
perm = getVariablePermission(varName);
kernel = current_task() == kernel_task;
if (perm.Bits.KernelOnly) {
DEBUG_INFO("KernelOnly access for %s, kernel=%d\n", varName, kernel);
ok = kernel;
goto exit;
}
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);
systemInternalEntitled = IOTaskHasEntitlement(current_task(), kIONVRAMSystemInternalAllowKey);
systemReadHiddenAllow = IOTaskHasEntitlement(current_task(), kIONVRAMSystemHiddenAllowKey);
systemAllow = systemEntitled || (systemInternalEntitled && gInternalBuild) || kernel;
switch (op) {
case kIONVRAMOperationRead:
if (systemGuid && perm.Bits.SystemReadHidden) {
ok = systemReadHiddenAllow;
} else if (kernel || admin || readEntitled || perm.Bits.FullAccess) {
ok = true;
}
break;
case kIONVRAMOperationWrite:
if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
if (systemGuid) {
if (allowList) {
if (!systemAllow) {
DEBUG_ERROR("Allowed write to system region when NOT entitled for %s\n", varName);
}
} else if (!systemAllow) {
DEBUG_ERROR("Not entitled for system region writes for %s\n", varName);
break;
}
}
ok = 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;
} else if ((op == kIONVRAMOperationDelete) && perm.Bits.ResetNVRAMOnlyDelete) {
DEBUG_INFO("Only allowed to delete %s via NVRAM reset\n", varName);
break;
}
if (kernel || perm.Bits.UserWrite || admin || writeEntitled) {
if (systemGuid) {
if (allowList) {
if (!systemAllow) {
DEBUG_ERROR("Allowed delete to system region when NOT entitled for %s\n", varName);
}
} else if (!systemAllow) {
DEBUG_ERROR("Not entitled for system region deletes for %s\n", varName);
break;
}
}
ok = true;
}
break;
}
exit:
DEBUG_INFO("Permission for %s of %s %s: kernel=%d, admin=%d, writeEntitled=%d, readEntitled=%d, systemGuid=%d, systemEntitled=%d, systemInternalEntitled=%d, systemReadHiddenAllow=%d, UserWrite=%d\n",
getNVRAMOpString(op), varName, ok ? "granted" : "denied", kernel, admin, writeEntitled, readEntitled, systemGuid, systemEntitled, systemInternalEntitled, systemReadHiddenAllow, perm.Bits.UserWrite);
return ok;
}
static bool
verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const OSSymbol *varName)
{
return verifyPermission(op, varGuid, varName->getCStringNoCopy());
}
/*
* 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)
{
uuid_string_t temp = {0};
size_t keyLen = strlen(key);
bool ok = 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);
ok = true;
}
}
if (guidResult) {
ok ? uuid_copy(*guidResult, guid) : uuid_copy(*guidResult, gAppleNVRAMGuid);
}
if (nameResult) {
*nameResult = name;
}
return false;
}
static bool
skipKey(const OSSymbol *aKey)
{
return aKey->isEqualTo(kIOClassNameOverrideKey) ||
aKey->isEqualTo(kIOBSDNameKey) ||
aKey->isEqualTo(kIOBSDNamesKey) ||
aKey->isEqualTo(kIOBSDMajorKey) ||
aKey->isEqualTo(kIOBSDMinorKey) ||
aKey->isEqualTo(kIOBSDUnitKey);
}
// ************************** IODTNVRAMVariables ****************************
// 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
{
OSDeclareDefaultStructors(IODTNVRAMVariables)
private:
IODTNVRAM *_provider;
OSDictionary *_variables;
uuid_t _guid;
public:
bool init(const uuid_t *guid);
virtual bool start(IOService * provider) APPLE_KEXT_OVERRIDE;
virtual IOReturn setVariables(OSObject * properties);
virtual bool serializeProperties(OSSerialize *s) const APPLE_KEXT_OVERRIDE;
virtual OSPtr<OSObject> copyProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
virtual OSObject *getProperty(const OSSymbol *aKey) const APPLE_KEXT_OVERRIDE;
virtual bool setProperty(const OSSymbol *aKey, OSObject *anObject) APPLE_KEXT_OVERRIDE;
virtual IOReturn setProperties(OSObject *properties) APPLE_KEXT_OVERRIDE;
virtual void removeProperty(const OSSymbol *aKey) APPLE_KEXT_OVERRIDE;
};
OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
bool
IODTNVRAMVariables::init(const uuid_t *guid)
{
if (!super::init()) {
return false;
}
if (guid == nullptr) {
return false;
}
uuid_copy(_guid, *guid);
return true;
}
bool
IODTNVRAMVariables::start(IOService * provider)
{
if (!IOService::start(provider)) {
goto error;
}
_provider = OSDynamicCast(IODTNVRAM, provider);
if (_provider == nullptr) {
goto error;
}
registerService();
return true;
error:
stop(provider);
return false;
}
IOReturn
IODTNVRAMVariables::setVariables(OSObject * variables)
{
if (OSDynamicCast(OSDictionary, variables)) {
OSSafeReleaseNULL(_variables);
_variables = OSDynamicCast(OSDictionary, variables);
variables->retain();
}
return kIOReturnSuccess;
}
bool
IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
{
const OSSymbol *key;
OSSharedPtr<OSDictionary> dict;
OSSharedPtr<OSCollectionIterator> iter;
OSSharedPtr<OSDictionary> localVariables(_variables, OSRetain);
bool ok = false;
if (localVariables == nullptr) {
goto exit;
}
dict = OSDictionary::withCapacity(localVariables->getCount());
if (dict == nullptr) {
DEBUG_ERROR("No dictionary\n");
goto exit;
}
iter = OSCollectionIterator::withCollection(localVariables.get());
if (iter == nullptr) {
DEBUG_ERROR("failed to create iterator\n");
goto exit;
}
while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
if (verifyPermission(kIONVRAMOperationRead, &_guid, key)) {
dict->setObject(key, localVariables->getObject(key));
}
}
ok = dict->serialize(s);
exit:
DEBUG_INFO("ok=%d\n", ok);
return ok;
}
OSPtr<OSObject>
IODTNVRAMVariables::copyProperty(const OSSymbol *aKey) const
{
if (_provider && !skipKey(aKey)) {
DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
return _provider->copyPropertyWithGUIDAndName(&_guid, aKey->getCStringNoCopy());
} else {
return nullptr;
}
}
OSObject *
IODTNVRAMVariables::getProperty(const OSSymbol *aKey) const
{
OSSharedPtr<OSObject> theObject = copyProperty(aKey);
return theObject.get();
}
bool
IODTNVRAMVariables::setProperty(const OSSymbol *aKey, OSObject *anObject)
{
if (_provider) {
return _provider->setPropertyWithGUIDAndName(&_guid, aKey->getCStringNoCopy(), anObject);
} else {
return false;
}
}
IOReturn
IODTNVRAMVariables::setProperties(OSObject *properties)
{
IOReturn ret = kIOReturnSuccess;
OSObject *object;
const OSSymbol *key;
OSDictionary *dict;
OSSharedPtr<OSCollectionIterator> iter;
if (_provider) {
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 (ret == kIOReturnSuccess) {
key = OSDynamicCast(OSSymbol, iter->getNextObject());
if (key == nullptr) {
break;
}
object = dict->getObject(key);
if (object == nullptr) {
continue;
}
ret = setProperty(key, object);
}
} else {
ret = kIOReturnNotReady;
}
DEBUG_INFO("ret=%#08x\n", ret);
return ret;
}
void
IODTNVRAMVariables::removeProperty(const OSSymbol *aKey)
{
if (_provider) {
_provider->removePropertyWithGUIDAndName(&_guid, aKey->getCStringNoCopy());
}
}
// **************************** IODTNVRAM *********************************
bool
IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
{
OSSharedPtr<OSDictionary> dict;
if (!super::init(old, plane)) {
return false;
}
PE_parse_boot_argn("nvram-log", &gNVRAMLogging, sizeof(gNVRAMLogging));
#if XNU_TARGET_OS_OSX
#if CONFIG_CSR
gInternalBuild = (csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0);
#endif // CONFIG_CSR
#endif // XNU_TARGET_OS_OSX
DEBUG_INFO("gInternalBuild = %d\n", gInternalBuild);
_variableLock = IORWLockAlloc();
if (!_variableLock) {
return false;
}
_controllerLock = IOLockAlloc();
if (!_controllerLock) {
return false;
}
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;
}
// <rdar://problem/9529235> 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;
}
void
IODTNVRAM::initProxyData(void)
{
OSSharedPtr<IORegistryEntry> entry;
const char *key = "nvram-proxy-data";
OSData *data;
const void *bytes;
entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
if (entry != nullptr) {
OSSharedPtr<OSObject> 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);
}
}
UInt32
IODTNVRAM::getNVRAMSize(void)
{
OSSharedPtr<IORegistryEntry> entry;
const char *key = "nvram-total-size";
OSData *data;
UInt32 size = 0;
entry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
if (entry != nullptr) {
OSSharedPtr<OSObject> 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;
}
void
IODTNVRAM::registerNVRAMController(IONVRAMController *nvram)
{
IOReturn ret;
if (_nvramController != nullptr) {
DEBUG_ERROR("Duplicate controller set\n");
return;
}
DEBUG_INFO("setting controller\n");
CONTROLLERLOCK();
_nvramController = nvram;
CONTROLLERUNLOCK();
// <rdar://problem/9529235> race condition possible between
// IODTNVRAM and IONVRAMController (restore loses boot-args)
if (!_isProxied) {
DEBUG_INFO("Reading non-proxied NVRAM data\n");
_nvramController->read(0, _nvramImage, _nvramSize);
initNVRAMImage();
}
if (_systemPartitionSize) {
_systemService = new IODTNVRAMVariables;
if (!_systemService || !_systemService->init(&gAppleSystemVariableGuid)) {
DEBUG_ERROR("Unable to start the system service!\n");
goto no_system;
}
_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;
}
}
no_common:
ret = serializeVariables();
DEBUG_INFO("serializeVariables ret=%#08x\n", ret);
}
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);
const uint8_t common_v1_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_COMMON_V1};
const uint8_t common_v2_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_COMMON_V2};
const uint8_t system_v1_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_SYSTEM_V1};
const uint8_t system_v2_name[sizeof(header->name)] = {NVRAM_CHRP_PARTITION_NAME_SYSTEM_V2};
currentLength = header->len * NVRAM_CHRP_LENGTH_BLOCK_SIZE;
if (currentLength < sizeof(chrp_nvram_header_t)) {
break;
}
partitionOffset = currentOffset + sizeof(chrp_nvram_header_t);
partitionLength = currentLength - sizeof(chrp_nvram_header_t);
if ((partitionOffset + partitionLength) > _nvramSize) {
break;
}
common_partition = (memcmp(header->name, common_v1_name, sizeof(header->name)) == 0) ||
(memcmp(header->name, common_v2_name, sizeof(header->name)) == 0);
system_partition = (memcmp(header->name, system_v1_name, sizeof(header->name)) == 0) ||
(memcmp(header->name, system_v2_name, sizeof(header->name)) == 0);
if (common_partition) {
_commonPartitionOffset = partitionOffset;
_commonPartitionSize = partitionLength;
} else if (system_partition) {
_systemPartitionOffset = partitionOffset;
_systemPartitionSize = partitionLength;
} else {
OSSharedPtr<OSNumber> partitionOffsetNumber, partitionLengthNumber;
// Construct the partition ID from the signature and name.
snprintf(partitionID, sizeof(partitionID), "%#02x,", header->sig);
memcpy(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 : commonPartitionOffset - %#x, commonPartitionSize - %#x, systemPartitionOffset - %#x, systemPartitionSize - %#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();
}
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();
}
void
IODTNVRAM::sync(void)
{
syncInternal(false);
}
bool
IODTNVRAM::serializeProperties(OSSerialize *s) const
{
const OSSymbol *key;
OSSharedPtr<OSDictionary> systemDict, commonDict, dict;
OSSharedPtr<OSCollectionIterator> iter;
bool ok = false;
unsigned int totalCapacity = 0;
NVRAMREADLOCK();
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);
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));
}
}
}
ok = dict->serialize(s);
exit:
DEBUG_INFO("ok=%d\n", ok);
return ok;
}
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();
}
return kIOReturnSuccess;
} else if (_commonDict != nullptr) {
DEBUG_INFO("Defaulting to common dictionary\n");
*dict = _commonDict.get();
return kIOReturnSuccess;
}
return kIOReturnNotFound;
}
IOReturn
IODTNVRAM::flushDict(const uuid_t *guid, IONVRAMOperation op)
{
IOReturn err = kIOReturnSuccess;
if ((_systemDict != nullptr) && (uuid_compare(*guid, gAppleSystemVariableGuid) == 0)) {
const OSSymbol *key;
OSSharedPtr<OSDictionary> newDict;
OSSharedPtr<OSCollectionIterator> iter;
newDict = OSDictionary::withCapacity(_systemDict->getCapacity());
iter = OSCollectionIterator::withCollection(_systemDict.get());
if ((newDict == nullptr) || (iter == nullptr)) {
err = kIOReturnNoMemory;
goto exit;
}
while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
if (!verifyPermission(op, &gAppleSystemVariableGuid, key)) {
newDict->setObject(key, _systemDict->getObject(key));
}
}
_systemDict = newDict;
DEBUG_INFO("system dictionary flushed\n");
} else if ((_commonDict != nullptr) && (uuid_compare(*guid, gAppleNVRAMGuid) == 0)) {
const OSSymbol *key;
OSSharedPtr<OSDictionary> newDict;
OSSharedPtr<OSCollectionIterator> iter;
newDict = OSDictionary::withCapacity(_commonDict->getCapacity());
iter = OSCollectionIterator::withCollection(_commonDict.get());
if ((newDict == nullptr) || (iter == nullptr)) {
err = kIOReturnNoMemory;
goto exit;
}
while ((key = OSDynamicCast(OSSymbol, iter->getNextObject()))) {
if (!verifyPermission(op, &gAppleNVRAMGuid, key)) {
newDict->setObject(key, _commonDict->getObject(key));
}
}
_commonDict = newDict;
DEBUG_INFO("common dictionary flushed\n");
}
exit:
return err;
}
bool
IODTNVRAM::handleSpecialVariables(const char *name, const uuid_t *guid, const OSObject *obj, IOReturn *error)
{
IOReturn err = kIOReturnSuccess;
bool special = false;
NVRAMLOCKASSERTEXCLUSIVE();
// ResetNVRam flushes both regions in one call
// Obliterate can flush either separately
if (strcmp(name, "ObliterateNVRam") == 0) {
err = flushDict(guid, kIONVRAMOperationObliterate);
} else if (strcmp(name, "ResetNVRam") == 0) {
err = flushDict(&gAppleSystemVariableGuid, kIONVRAMOperationReset);
if (err != kIOReturnSuccess) {
goto exit;
}
err = flushDict(&gAppleNVRAMGuid, kIONVRAMOperationReset);
}
exit:
if (error) {
*error = err;
}
return special;
}
OSSharedPtr<OSObject>
IODTNVRAM::copyPropertyWithGUIDAndName(const uuid_t *guid, const char *name) const
{
IOReturn result;
OSDictionary *dict;
OSSharedPtr<OSObject> theObject = nullptr;
result = chooseDictionary(kIONVRAMOperationRead, guid, name, &dict);
if (result != kIOReturnSuccess) {
DEBUG_INFO("No dictionary\n");
goto exit;
}
if (!verifyPermission(kIONVRAMOperationRead, guid, name)) {
DEBUG_INFO("Not privileged\n");
goto exit;
}
NVRAMREADLOCK();
theObject.reset(dict->getObject(name), OSRetain);
NVRAMUNLOCK();
if (theObject != nullptr) {
DEBUG_INFO("found data\n");
}
exit:
return theObject;
}
OSSharedPtr<OSObject>
IODTNVRAM::copyProperty(const OSSymbol *aKey) const
{
const char *variableName;
uuid_t varGuid;
if (skipKey(aKey)) {
return nullptr;
}
DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
return copyPropertyWithGUIDAndName(&varGuid, variableName);
}
OSSharedPtr<OSObject>
IODTNVRAM::copyProperty(const char *aKey) const
{
OSSharedPtr<const OSSymbol> keySymbol;
OSSharedPtr<OSObject> theObject;
keySymbol = OSSymbol::withCString(aKey);
if (keySymbol != nullptr) {
theObject = copyProperty(keySymbol.get());
}
return theObject;
}
OSObject *
IODTNVRAM::getProperty(const OSSymbol *aKey) const
{
// The shared pointer gets released at the end of the function,
// and returns a view into theObject.
OSSharedPtr<OSObject> theObject = copyProperty(aKey);
return theObject.get();
}
OSObject *
IODTNVRAM::getProperty(const char *aKey) const
{
// The shared pointer gets released at the end of the function,
// and returns a view into theObject.
OSSharedPtr<OSObject> theObject = copyProperty(aKey);
return theObject.get();
}
IOReturn
IODTNVRAM::setPropertyWithGUIDAndName(const uuid_t *guid, const char *name, OSObject *anObject)
{
IOReturn ret = kIOReturnSuccess;
bool remove = false;
OSString *tmpString = nullptr;
OSSharedPtr<OSObject> propObject, oldObject;
OSSharedPtr<OSObject> sharedObject(anObject, OSRetain);
OSDictionary *dict;
bool deletePropertyKey, syncNowPropertyKey, forceSyncNowPropertyKey;
bool ok;
size_t propDataSize = 0;
deletePropertyKey = strncmp(name, kIONVRAMDeletePropertyKey, sizeof(kIONVRAMDeletePropertyKey)) == 0;
syncNowPropertyKey = strncmp(name, kIONVRAMSyncNowPropertyKey, sizeof(kIONVRAMSyncNowPropertyKey)) == 0;
forceSyncNowPropertyKey = strncmp(name, kIONVRAMForceSyncNowPropertyKey, sizeof(kIONVRAMForceSyncNowPropertyKey)) == 0;
if (deletePropertyKey) {
tmpString = OSDynamicCast(OSString, anObject);
if (tmpString != nullptr) {
const char *variableName;
uuid_t varGuid;
DEBUG_INFO("kIONVRAMDeletePropertyKey found\n");
parseVariableName(tmpString->getCStringNoCopy(), &varGuid, &variableName);
removePropertyWithGUIDAndName(&varGuid, variableName);
} else {
DEBUG_INFO("kIONVRAMDeletePropertyKey value needs to be an OSString\n");
ret = kIOReturnError;
}
goto exit;
} else if (syncNowPropertyKey || forceSyncNowPropertyKey) {
tmpString = OSDynamicCast(OSString, anObject);
DEBUG_INFO("NVRAM sync key %s found\n", name);
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", name);
ret = kIOReturnError;
}
goto exit;
}
ret = chooseDictionary(kIONVRAMOperationWrite, guid, name, &dict);
if (ret != kIOReturnSuccess) {
DEBUG_INFO("No dictionary\n");
goto exit;
}
if (!verifyPermission(kIONVRAMOperationWrite, guid, name)) {
DEBUG_INFO("Not privileged\n");
ret = kIOReturnNotPrivileged;
goto exit;
}
// Make sure the object is of the correct type.
switch (getVariableType(name)) {
case kOFVariableTypeBoolean:
propObject = OSDynamicPtrCast<OSBoolean>(sharedObject);
break;
case kOFVariableTypeNumber:
propObject = OSDynamicPtrCast<OSNumber>(sharedObject);
break;
case kOFVariableTypeString:
propObject = OSDynamicPtrCast<OSString>(sharedObject);
if (propObject != nullptr) {
propDataSize = (OSDynamicPtrCast<OSString>(propObject))->getLength();
if ((strncmp(name, kIONVRAMBootArgsKey, sizeof(kIONVRAMBootArgsKey)) == 0) && (propDataSize >= BOOT_LINE_LENGTH)) {
DEBUG_ERROR("boot-args size too large for BOOT_LINE_LENGTH, propDataSize=%zu\n", propDataSize);
ret = kIOReturnNoSpace;
goto exit;
}
}
break;
case kOFVariableTypeData:
propObject = OSDynamicPtrCast<OSData>(sharedObject);
if (propObject == nullptr) {
tmpString = OSDynamicCast(OSString, sharedObject.get());
if (tmpString != nullptr) {
propObject = OSData::withBytes(tmpString->getCStringNoCopy(),
tmpString->getLength());
}
}
if (propObject != nullptr) {
propDataSize = (OSDynamicPtrCast<OSData>(propObject))->getLength();
}
#if defined(XNU_TARGET_OS_OSX)
if ((propObject != nullptr) && ((OSDynamicPtrCast<OSData>(propObject))->getLength() == 0)) {
remove = true;
}
#endif /* defined(XNU_TARGET_OS_OSX) */
break;
default:
break;
}
if (propObject == nullptr) {
DEBUG_INFO("No property object\n");
ret = kIOReturnBadArgument;
goto exit;
}
if (!verifyWriteSizeLimit(guid, name, propDataSize)) {
DEBUG_ERROR("Property data size of %zu too long for %s\n", propDataSize, name);
ret = kIOReturnNoSpace;
goto exit;
}
NVRAMWRITELOCK();
ok = handleSpecialVariables(name, guid, propObject.get(), &ret);
NVRAMUNLOCK();
if (ok) {
serializeVariables();
goto exit;
}
NVRAMREADLOCK();
oldObject.reset(dict->getObject(name), OSRetain);
NVRAMUNLOCK();
if (remove == false) {
DEBUG_INFO("Adding object\n");
NVRAMWRITELOCK();
if (!dict->setObject(name, propObject.get())) {
ret = kIOReturnBadArgument;
}
NVRAMUNLOCK();
} else {
DEBUG_INFO("Removing object\n");
// Check for existence so we can decide whether we need to sync variables
if (oldObject) {
ret = removePropertyWithGUIDAndName(guid, name);
} else {
ret = kIOReturnNotFound;
}
}
if (ret == kIOReturnSuccess) {
ret = serializeVariables();
if (ret != kIOReturnSuccess) {
DEBUG_ERROR("serializeVariables failed, ret=%#08x\n", ret);
NVRAMWRITELOCK();
if (oldObject) {
dict->setObject(name, oldObject.get());
} else {
dict->removeObject(name);
}
NVRAMUNLOCK();
(void) serializeVariables();
ret = kIOReturnNoMemory;
}
}
if (oldObject) {
oldObject.reset();
}
if (tmpString) {
propObject.reset();
}
exit:
DEBUG_INFO("ret=%#08x\n", ret);
return ret;
}
IOReturn
IODTNVRAM::setPropertyInternal(const OSSymbol *aKey, OSObject *anObject)
{
const char *variableName;
uuid_t varGuid;
DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
return setPropertyWithGUIDAndName(&varGuid, variableName, anObject);
}
bool
IODTNVRAM::setProperty(const OSSymbol *aKey, OSObject *anObject)
{
return setPropertyInternal(aKey, anObject) == kIOReturnSuccess;
}
void
IODTNVRAM::removeProperty(const OSSymbol *aKey)
{
IOReturn ret;
ret = removePropertyInternal(aKey);
if (ret == kIOReturnSuccess) {
serializeVariables();
} else {
DEBUG_INFO("removePropertyInternal failed, ret=%#08x\n", ret);
}
}
IOReturn
IODTNVRAM::removePropertyWithGUIDAndName(const uuid_t *guid, const char *name)
{
IOReturn ret;
OSDictionary *dict;
bool removed = false;
DEBUG_INFO("name=%s\n", name);
ret = chooseDictionary(kIONVRAMOperationDelete, guid, name, &dict);
if (ret != kIOReturnSuccess) {
DEBUG_INFO("No dictionary\n");
goto exit;
}
if (!verifyPermission(kIONVRAMOperationDelete, guid, name)) {
DEBUG_INFO("Not priveleged\n");
ret = kIOReturnNotPrivileged;
goto exit;
}
NVRAMWRITELOCK();
// If the object exists, remove it from the dictionary.
if (dict->getObject(name) != nullptr) {
dict->removeObject(name);
removed = true;
} else {
DEBUG_INFO("%s not found\n", name);
}
NVRAMUNLOCK();
if (removed) {
ret = serializeVariables();
DEBUG_INFO("serializeVariables ret=0x%08x\n", ret);
}
exit:
return ret;
}
IOReturn
IODTNVRAM::removePropertyInternal(const OSSymbol *aKey)
{
IOReturn ret;
const char *variableName;
uuid_t varGuid;
DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
ret = removePropertyWithGUIDAndName(&varGuid, variableName);
return ret;
}
IOReturn
IODTNVRAM::setProperties(OSObject *properties)
{
IOReturn ret = kIOReturnSuccess;
OSObject *object;
const OSSymbol *key;
OSDictionary *dict;
OSSharedPtr<OSCollectionIterator> iter;
dict = OSDynamicCast(OSDictionary, properties);
if (dict == nullptr) {
DEBUG_ERROR("Not a dictionary\n");
return kIOReturnBadArgument;
}
iter = OSCollectionIterator::withCollection(dict);
if (iter == nullptr) {
DEBUG_ERROR("Couldn't create iterator\n");
return kIOReturnBadArgument;
}
while (ret == kIOReturnSuccess) {
key = OSDynamicCast(OSSymbol, iter->getNextObject());
if (key == nullptr) {
break;
}
object = dict->getObject(key);
if (object == nullptr) {
continue;
}
ret = setPropertyInternal(key, object);
}
DEBUG_INFO("ret=%#08x\n", ret);
return ret;
}
IOReturn
IODTNVRAM::readXPRAM(IOByteCount offset, UInt8 *buffer,
IOByteCount length)
{
return kIOReturnUnsupported;
}
IOReturn
IODTNVRAM::writeXPRAM(IOByteCount offset, UInt8 *buffer,
IOByteCount length)
{
return kIOReturnUnsupported;
}
IOReturn
IODTNVRAM::readNVRAMProperty(IORegistryEntry *entry,
const OSSymbol **name,
OSData **value)
{
IOReturn err;
err = readNVRAMPropertyType1(entry, name, value);
return err;
}
IOReturn
IODTNVRAM::writeNVRAMProperty(IORegistryEntry *entry,
const OSSymbol *name,
OSData *value)
{
IOReturn err;
err = writeNVRAMPropertyType1(entry, name, value);
return err;
}
OSDictionary *
IODTNVRAM::getNVRAMPartitions(void)
{
return _nvramPartitionLengths.get();
}
IOReturn
IODTNVRAM::readNVRAMPartition(const OSSymbol *partitionID,
IOByteCount offset, UInt8 *buffer,
IOByteCount length)
{
OSNumber *partitionOffsetNumber, *partitionLengthNumber;
UInt32 partitionOffset, partitionLength, end;
partitionOffsetNumber =
(OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
partitionLengthNumber =
(OSNumber *)_nvramPartitionLengths->getObject(partitionID);
if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
return kIOReturnNotFound;
}
partitionOffset = partitionOffsetNumber->unsigned32BitValue();
partitionLength = partitionLengthNumber->unsigned32BitValue();
if (os_add_overflow(offset, length, &end)) {
return kIOReturnBadArgument;
}
if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
return kIOReturnBadArgument;
}
bcopy(_nvramImage + partitionOffset + offset, buffer, length);
return kIOReturnSuccess;
}
IOReturn
IODTNVRAM::writeNVRAMPartition(const OSSymbol *partitionID,
IOByteCount offset, UInt8 *buffer,
IOByteCount length)
{
OSNumber *partitionOffsetNumber, *partitionLengthNumber;
UInt32 partitionOffset, partitionLength, end;
partitionOffsetNumber =
(OSNumber *)_nvramPartitionOffsets->getObject(partitionID);
partitionLengthNumber =
(OSNumber *)_nvramPartitionLengths->getObject(partitionID);
if ((partitionOffsetNumber == nullptr) || (partitionLengthNumber == nullptr)) {
return kIOReturnNotFound;
}
partitionOffset = partitionOffsetNumber->unsigned32BitValue();
partitionLength = partitionLengthNumber->unsigned32BitValue();
if (os_add_overflow(offset, length, &end)) {
return kIOReturnBadArgument;
}
if ((buffer == nullptr) || (length == 0) || (end > partitionLength)) {
return kIOReturnBadArgument;
}
bcopy(buffer, _nvramImage + partitionOffset + offset, length);
if (_nvramController != nullptr) {
_nvramController->write(0, _nvramImage, _nvramSize);
}
return kIOReturnSuccess;
}
IOByteCount
IODTNVRAM::savePanicInfo(UInt8 *buffer, IOByteCount length)
{
return 0;
}
// Private methods
UInt8
IODTNVRAM::calculatePartitionChecksum(UInt8 *partitionHeader)
{
UInt8 cnt, isum, csum = 0;
for (cnt = 0; cnt < 0x10; cnt++) {
isum = csum + partitionHeader[cnt];
if (isum < csum) {
isum++;
}
csum = isum;
}
return csum;
}
IOReturn
IODTNVRAM::initVariables(void)
{
UInt32 cnt;
UInt8 *propName, *propData;
UInt32 propNameLength, propDataLength, regionIndex;
OSSharedPtr<const OSSymbol> propSymbol;
OSSharedPtr<OSObject> propObject;
NVRAMRegionInfo *currentRegion;
NVRAMRegionInfo variableRegions[] = { { kIONVRAMPartitionCommon, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
{ kIONVRAMPartitionSystem, _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 = %d\n", currentRegion->type);
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 (_systemDict != nullptr) {
if (_systemDict->getObject(kIONVRAMBootArgsKey) == nullptr) {
propObject = OSString::withCStringNoCopy("");
if (propObject != nullptr) {
_systemDict->setObject(kIONVRAMBootArgsKey, propObject.get());
}
}
} else if (_commonDict != nullptr) {
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 ? _commonDict.get() : nullptr, _systemDict ? _systemDict.get() : nullptr);
return kIOReturnSuccess;
}
IOReturn
IODTNVRAM::syncOFVariables(void)
{
return kIOReturnUnsupported;
}
IOReturn
IODTNVRAM::serializeVariables(void)
{
IOReturn ret;
bool ok;
UInt32 length, maxLength, regionIndex;
UInt8 *buffer, *tmpBuffer;
const OSSymbol *tmpSymbol;
OSObject *tmpObject;
OSSharedPtr<OSCollectionIterator> iter;
OSSharedPtr<OSNumber> sizeUsed;
UInt32 systemUsed = 0;
UInt32 commonUsed = 0;
OSSharedPtr<OSData> nvramImage;
NVRAMRegionInfo *currentRegion;
NVRAMRegionInfo variableRegions[] = { { kIONVRAMPartitionCommon, _commonPartitionOffset, _commonPartitionSize, _commonDict, _commonImage},
{ kIONVRAMPartitionSystem, _systemPartitionOffset, _systemPartitionSize, _systemDict, _systemImage} };
if (_systemPanicked) {
return kIOReturnNotReady;
}
if (_nvramController == nullptr) {
DEBUG_ERROR("No _nvramController\n");
return kIOReturnNotReady;
}
DEBUG_INFO("...\n");
NVRAMREADLOCK();
for (regionIndex = 0; regionIndex < ARRAY_SIZE(variableRegions); regionIndex++) {
currentRegion = &variableRegions[regionIndex];
if (currentRegion->size == 0) {
continue;
}
DEBUG_INFO("region = %d\n", currentRegion->type);
buffer = tmpBuffer = IONew(UInt8, currentRegion->size);
if (buffer == nullptr) {
ok = false;
ret = kIOReturnNoMemory;
break;
}
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);
}
if ((currentRegion->type == kIONVRAMPartitionSystem) &&
(_systemService != nullptr)) {
_systemService->setVariables(_systemDict.get());
systemUsed = (uint32_t)(tmpBuffer - buffer);
} else if ((currentRegion->type == kIONVRAMPartitionCommon) &&
(_commonService != nullptr)) {
_commonService->setVariables(_commonDict.get());
commonUsed = (uint32_t)(tmpBuffer - buffer);
}
IODelete(buffer, UInt8, currentRegion->size);
if (!ok) {
ret = kIOReturnBadArgument;
break;
}
}
NVRAMUNLOCK();
DEBUG_INFO("ok=%d\n", ok);
if (ok) {
nvramImage = OSData::withBytes(_nvramImage, _nvramSize);
CONTROLLERLOCK();
if (_systemService) {
sizeUsed = OSNumber::withNumber(systemUsed, 32);
_nvramController->setProperty("SystemUsed", sizeUsed.get());
DEBUG_INFO("SystemUsed=%u\n", (unsigned int)commonUsed);
sizeUsed.reset();
}
if (_commonService) {
sizeUsed = OSNumber::withNumber(commonUsed, 32);
_nvramController->setProperty("CommonUsed", sizeUsed.get());
DEBUG_INFO("CommonUsed=%u\n", (unsigned int)commonUsed);
sizeUsed.reset();
}
ret = _nvramController->write(0, (uint8_t *)nvramImage->getBytesNoCopy(), nvramImage->getLength());
CONTROLLERUNLOCK();
}
return ret;
}
UInt32
IODTNVRAM::getOFVariableType(const char *propName) const
{
return 0;
}
UInt32
IODTNVRAM::getOFVariableType(const OSSymbol *propSymbol) const
{
return 0;
}
UInt32
IODTNVRAM::getOFVariablePerm(const char *propName) const
{
return 0;
}
UInt32
IODTNVRAM::getOFVariablePerm(const OSSymbol *propSymbol) const
{
return 0;
}
bool
IODTNVRAM::getOWVariableInfo(UInt32 variableNumber, const OSSymbol **propSymbol,
UInt32 *propType, UInt32 *propOffset)
{
/* UNSUPPORTED */
return false;
}
bool
IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
UInt8 *propData, UInt32 propDataLength,
const OSSymbol **propSymbol,
OSObject **propObject)
{
OSSharedPtr<const OSSymbol> tmpSymbol;
OSSharedPtr<OSNumber> tmpNumber;
OSSharedPtr<OSString> tmpString;
OSSharedPtr<OSObject> tmpObject = nullptr;
propName[propNameLength] = '\0';
tmpSymbol = OSSymbol::withCString((const char *)propName);
propName[propNameLength] = '=';
if (tmpSymbol == nullptr) {
return false;
}
switch (getVariableType(tmpSymbol.get())) {
case kOFVariableTypeBoolean:
if (!strncmp("true", (const char *)propData, propDataLength)) {
tmpObject.reset(kOSBooleanTrue, OSRetain);
} else if (!strncmp("false", (const char *)propData, propDataLength)) {
tmpObject.reset(kOSBooleanFalse, OSRetain);
}
break;
case kOFVariableTypeNumber:
tmpNumber = OSNumber::withNumber(strtol((const char *)propData, nullptr, 0), 32);
if (tmpNumber != nullptr) {
tmpObject = tmpNumber;
}
break;
case kOFVariableTypeString:
tmpString = OSString::withCString((const char *)propData);
if (tmpString != nullptr) {
tmpObject = tmpString;
}
break;
case kOFVariableTypeData:
tmpObject = unescapeBytesToData(propData, propDataLength);
break;
default:
break;
}
if (tmpObject == nullptr) {
tmpSymbol.reset();
return false;
}
*propSymbol = tmpSymbol.detach();
*propObject = tmpObject.detach();
return true;
}
bool
IODTNVRAM::convertPropToObject(UInt8 *propName, UInt32 propNameLength,
UInt8 *propData, UInt32 propDataLength,
OSSharedPtr<const OSSymbol>& propSymbol,
OSSharedPtr<OSObject>& propObject)
{
const OSSymbol* propSymbolRaw = nullptr;
OSObject* propObjectRaw = nullptr;
bool ok = convertPropToObject(propName, propNameLength, propData, propDataLength,
&propSymbolRaw, &propObjectRaw);
propSymbol.reset(propSymbolRaw, OSNoRetain);
propObject.reset(propObjectRaw, OSNoRetain);
return ok;
}
bool
IODTNVRAM::convertObjectToProp(UInt8 *buffer, UInt32 *length,
const OSSymbol *propSymbol, OSObject *propObject)
{
const UInt8 *propName;
UInt32 propNameLength, propDataLength, remaining;
IONVRAMVariableType propType;
OSBoolean *tmpBoolean = nullptr;
OSNumber *tmpNumber = nullptr;
OSString *tmpString = nullptr;
OSSharedPtr<OSData> tmpData;
propName = (const UInt8 *)propSymbol->getCStringNoCopy();
propNameLength = propSymbol->getLength();
propType = getVariableType(propSymbol);
// Get the size of the data.
propDataLength = 0xFFFFFFFF;
switch (propType) {
case kOFVariableTypeBoolean:
tmpBoolean = OSDynamicCast(OSBoolean, propObject);
if (tmpBoolean != nullptr) {
propDataLength = 5;
}
break;
case kOFVariableTypeNumber:
tmpNumber = OSDynamicCast(OSNumber, propObject);
if (tmpNumber != nullptr) {
propDataLength = 10;
}
break;
case kOFVariableTypeString:
tmpString = OSDynamicCast(OSString, propObject);
if (tmpString != nullptr) {
propDataLength = tmpString->getLength();
}
break;
case kOFVariableTypeData:
tmpData.reset(OSDynamicCast(OSData, propObject), OSNoRetain);
if (tmpData != nullptr) {
tmpData = escapeDataToData(tmpData.detach());
propDataLength = tmpData->getLength();
}
break;
default:
break;
}
// Make sure the propertySize is known and will fit.
if (propDataLength == 0xFFFFFFFF) {
return false;
}
if ((propNameLength + propDataLength + 2) > *length) {
return false;
}
// Copy the property name equal sign.
buffer += snprintf((char *)buffer, *length, "%s=", propName);
remaining = *length - propNameLength - 1;
switch (propType) {
case kOFVariableTypeBoolean:
if (tmpBoolean->getValue()) {
strlcpy((char *)buffer, "true", remaining);
} else {
strlcpy((char *)buffer, "false", remaining);
}
break;
case kOFVariableTypeNumber:
{
uint32_t tmpValue = tmpNumber->unsigned32BitValue();
if (tmpValue == 0xFFFFFFFF) {
strlcpy((char *)buffer, "-1", remaining);
} else if (tmpValue < 1000) {
snprintf((char *)buffer, remaining, "%d", (uint32_t)tmpValue);
} else {
snprintf((char *)buffer, remaining, "%#x", (uint32_t)tmpValue);
}
}
break;
case kOFVariableTypeString:
strlcpy((char *)buffer, tmpString->getCStringNoCopy(), remaining);
break;
case kOFVariableTypeData:
bcopy(tmpData->getBytesNoCopy(), buffer, propDataLength);
tmpData.reset();
break;
default:
break;
}
propDataLength = ((UInt32) strlen((const char *)buffer));
*length = propNameLength + propDataLength + 2;
return true;
}
UInt16
IODTNVRAM::generateOWChecksum(UInt8 *buffer)
{
UInt32 cnt, checksum = 0;
UInt16 *tmpBuffer = (UInt16 *)buffer;
for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
checksum += tmpBuffer[cnt];
}
return checksum % 0x0000FFFF;
}
bool
IODTNVRAM::validateOWChecksum(UInt8 *buffer)
{
UInt32 cnt, checksum, sum = 0;
UInt16 *tmpBuffer = (UInt16 *)buffer;
for (cnt = 0; cnt < _commonPartitionSize / 2; cnt++) {
sum += tmpBuffer[cnt];
}
checksum = (sum >> 16) + (sum & 0x0000FFFF);
if (checksum == 0x10000) {
checksum--;
}
checksum = (checksum ^ 0x0000FFFF) & 0x0000FFFF;
return checksum == 0;
}
void
IODTNVRAM::updateOWBootArgs(const OSSymbol *key, OSObject *value)
{
/* UNSUPPORTED */
}
bool
IODTNVRAM::searchNVRAMProperty(IONVRAMDescriptor *hdr, UInt32 *where)
{
return false;
}
IOReturn
IODTNVRAM::readNVRAMPropertyType0(IORegistryEntry *entry,
const OSSymbol **name,
OSData **value)
{
return kIOReturnUnsupported;
}
IOReturn
IODTNVRAM::writeNVRAMPropertyType0(IORegistryEntry *entry,
const OSSymbol *name,
OSData *value)
{
return kIOReturnUnsupported;
}
OSSharedPtr<OSData>
IODTNVRAM::unescapeBytesToData(const UInt8 *bytes, UInt32 length)
{
OSSharedPtr<OSData> data;
UInt32 totalLength = 0;
UInt32 cnt, cnt2;
UInt8 byte;
bool ok;
// Calculate the actual length of the data.
ok = true;
totalLength = 0;
for (cnt = 0; cnt < length;) {
byte = bytes[cnt++];
if (byte == 0xFF) {
byte = bytes[cnt++];
if (byte == 0x00) {
ok = false;
break;
}
cnt2 = byte & 0x7F;
} else {
cnt2 = 1;
}
totalLength += cnt2;
}
if (ok) {
// Create an empty OSData of the correct size.
data = OSData::withCapacity(totalLength);
if (data != nullptr) {
for (cnt = 0; cnt < length;) {
byte = bytes[cnt++];
if (byte == 0xFF) {
byte = bytes[cnt++];
cnt2 = byte & 0x7F;
byte = (byte & 0x80) ? 0xFF : 0x00;
} else {
cnt2 = 1;
}
data->appendByte(byte, cnt2);
}
}
}
return data;
}
OSSharedPtr<OSData>
IODTNVRAM::escapeDataToData(OSData * value)
{
OSSharedPtr<OSData> result;
const UInt8 *startPtr;
const UInt8 *endPtr;
const UInt8 *wherePtr;
UInt8 byte;
bool ok = true;
wherePtr = (const UInt8 *) value->getBytesNoCopy();
endPtr = wherePtr + value->getLength();
result = OSData::withCapacity((unsigned int) (endPtr - wherePtr));
if (!result) {
return result;
}
while (wherePtr < endPtr) {
startPtr = wherePtr;
byte = *wherePtr++;
if ((byte == 0x00) || (byte == 0xFF)) {
for (;
((wherePtr - startPtr) < 0x80) && (wherePtr < endPtr) && (byte == *wherePtr);
wherePtr++) {
}
ok &= result->appendByte(0xff, 1);
byte = (byte & 0x80) | ((UInt8)(wherePtr - startPtr));
}
ok &= result->appendByte(byte, 1);
}
ok &= result->appendByte(0, 1);
if (!ok) {
result.reset();
}
return result;
}
static bool
IsApplePropertyName(const char * propName)
{
char c;
while ((c = *propName++)) {
if ((c >= 'A') && (c <= 'Z')) {
break;
}
}
return c == 0;
}
IOReturn
IODTNVRAM::readNVRAMPropertyType1(IORegistryEntry *entry,
const OSSymbol **name,
OSData **value)
{
IOReturn err = kIOReturnNoResources;
OSData *data;
const UInt8 *startPtr;
const UInt8 *endPtr;
const UInt8 *wherePtr;
const UInt8 *nvPath = nullptr;
const char *nvName = nullptr;
const char *resultName = nullptr;
const UInt8 *resultValue = nullptr;
UInt32 resultValueLen = 0;
UInt8 byte;
NVRAMREADLOCK();
data = OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get()));
NVRAMUNLOCK();
if (data == nullptr) {
return err;
}
startPtr = (const UInt8 *) data->getBytesNoCopy();
endPtr = startPtr + data->getLength();
wherePtr = startPtr;
while (wherePtr < endPtr) {
byte = *(wherePtr++);
if (byte) {
continue;
}
if (nvPath == nullptr) {
nvPath = startPtr;
} else if (nvName == nullptr) {
nvName = (const char *) startPtr;
} else {
OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
if (entry == compareEntry) {
bool appleProp = IsApplePropertyName(nvName);
if (!appleProp || !resultName) {
resultName = nvName;
resultValue = startPtr;
resultValueLen = (UInt32) (wherePtr - startPtr - 1); // OSData getLength() is 32b
}
if (!appleProp) {
break;
}
}
nvPath = nullptr;
nvName = nullptr;
}
startPtr = wherePtr;
}
if (resultName) {
*name = OSSymbol::withCString(resultName).detach();
*value = unescapeBytesToData(resultValue, resultValueLen).detach();
if ((*name != nullptr) && (*value != nullptr)) {
err = kIOReturnSuccess;
} else {
err = kIOReturnNoMemory;
}
}
return err;
}
IOReturn
IODTNVRAM::writeNVRAMPropertyType1(IORegistryEntry *entry,
const OSSymbol *propName,
OSData *value)
{
OSSharedPtr<OSData> data, oldData;
const UInt8 *startPtr;
const UInt8 *propStart;
const UInt8 *endPtr;
const UInt8 *wherePtr;
const UInt8 *nvPath = nullptr;
const char *nvName = nullptr;
const char *comp;
const char *name;
UInt8 byte;
bool ok = true;
bool settingAppleProp;
settingAppleProp = IsApplePropertyName(propName->getCStringNoCopy());
// copy over existing properties for other entries
NVRAMWRITELOCK();
oldData.reset(OSDynamicCast(OSData, _commonDict->getObject(_registryPropertiesKey.get())), OSRetain);
if (oldData) {
startPtr = (const UInt8 *) oldData->getBytesNoCopy();
endPtr = startPtr + oldData->getLength();
propStart = startPtr;
wherePtr = startPtr;
while (wherePtr < endPtr) {
byte = *(wherePtr++);
if (byte) {
continue;
}
if (nvPath == nullptr) {
nvPath = startPtr;
} else if (nvName == nullptr) {
nvName = (const char *) startPtr;
} else {
OSSharedPtr<IORegistryEntry> compareEntry = IORegistryEntry::fromPath((const char *) nvPath, gIODTPlane);
if (entry == compareEntry) {
if ((settingAppleProp && propName->isEqualTo(nvName))
|| (!settingAppleProp && !IsApplePropertyName(nvName))) {
// delete old property (nvPath -> wherePtr) source OSData len is 32b
data = OSData::withBytes(propStart, (UInt32)(nvPath - propStart));
if (data) {
ok &= data->appendBytes(wherePtr, (UInt32)(endPtr - wherePtr));
}
break;
}
}
nvPath = nullptr;
nvName = nullptr;
}
startPtr = wherePtr;
}
}
// make the new property
if (!data) {
if (oldData) {
data = OSData::withData(oldData.get());
} else {
data = OSData::withCapacity(16);
}
if (!data) {
ok = false;
}
}
if (ok && value && value->getLength()) {
do {
// get entries in path
OSSharedPtr<OSArray> array = OSArray::withCapacity(5);
if (!array) {
ok = false;
break;
}
do{
array->setObject(entry);
} while ((entry = entry->getParentEntry(gIODTPlane)));
// append path
for (int i = array->getCount() - 3;
(entry = (IORegistryEntry *) array->getObject(i));
i--) {
name = entry->getName(gIODTPlane);
comp = entry->getLocation(gIODTPlane);
if (comp) {
ok &= data->appendBytes("/@", 2);
} else {
if (!name) {
continue;
}
ok &= data->appendByte('/', 1);
comp = name;
}
ok &= data->appendBytes(comp, (unsigned int) strnlen(comp, UINT16_MAX));
}
ok &= data->appendByte(0, 1);
// append prop name
ok &= data->appendBytes(propName->getCStringNoCopy(), propName->getLength() + 1);
// append escaped data
OSSharedPtr<OSData> escapedData = escapeDataToData(value);
ok &= (escapedData != nullptr);
if (ok) {
ok &= data->appendBytes(escapedData.get());
}
} while (false);
}
if (ok) {
ok = _commonDict->setObject(_registryPropertiesKey.get(), data.get());
}
NVRAMUNLOCK();
if (ok) {
if (serializeVariables() != kIOReturnSuccess) {
NVRAMWRITELOCK();
if (oldData) {
_commonDict->setObject(_registryPropertiesKey.get(), oldData.get());
} else {
_commonDict->removeObject(_registryPropertiesKey.get());
}
NVRAMUNLOCK();
(void) serializeVariables();
ok = false;
}
}
oldData.reset();
return ok ? kIOReturnSuccess : kIOReturnNoMemory;
}
bool
IODTNVRAM::safeToSync(void)
{
AbsoluteTime delta;
UInt64 delta_ns;
SInt32 delta_secs;
// delta interval went by
clock_get_uptime(&delta);
// Figure it in seconds.
absolutetime_to_nanoseconds(delta, &delta_ns);
delta_secs = (SInt32)(delta_ns / NSEC_PER_SEC);
if ((delta_secs > (_lastDeviceSync + MIN_SYNC_NOW_INTERVAL)) || _freshInterval) {
_lastDeviceSync = delta_secs;
_freshInterval = FALSE;
return TRUE;
}
return FALSE;
}