+#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 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<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;
+
+// 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 <rdar://problem/70476321>
+ "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
+};
+
+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 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++;
+ }
+
+ 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;
+}
+
+static bool
+verifyPermission(IONVRAMOperation op, const uuid_t *varGuid, const char *varName)
+{
+ VariablePermission perm;
+ bool kernel, admin, writeEntitled, readEntitled, allowList, systemGuid, systemEntitled;
+
+ perm = getVariablePermission(varName);
+
+ 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;
+ }
+
+ 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;
+}
+
+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 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;
+ }
+
+ return false;
+}
+
+// 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 *_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;
+};
+
+OSDefineMetaClassAndStructors(IODTNVRAMVariables, IOService)
+
+bool
+IODTNVRAMVariables::init(const uuid_t *guid)
+{
+ require(super::init(), error);
+ require(guid, error);
+
+ uuid_copy(_guid, *guid);
+
+ return true;
+
+error:
+ return false;
+}
+
+bool
+IODTNVRAMVariables::start(IOService * provider)
+{
+ require(IOService::start(provider), error);
+
+ require(_provider = OSDynamicCast(IODTNVRAM, provider), error);
+
+ registerService();
+
+ return true;
+
+error:
+ stop(provider);
+
+ return false;
+}
+
+IOReturn
+IODTNVRAMVariables::setProperties(OSObject * properties)
+{
+ if (OSDynamicCast(OSDictionary, properties)) {
+ OSSafeReleaseNULL(_properties);
+ _properties = OSDynamicCast(OSDictionary, properties);
+ properties->retain();
+ }
+
+ return IOService::setProperties(properties);
+}
+
+bool
+IODTNVRAMVariables::serializeProperties(OSSerialize *s) const
+{
+ const OSSymbol *key;
+ OSSharedPtr<OSDictionary> dict;
+ OSSharedPtr<OSCollectionIterator> iter;
+ OSSharedPtr<OSDictionary> localProperties(_properties, OSRetain);
+ bool result = false;
+
+ require(localProperties != nullptr, exit);
+
+ dict = OSDictionary::withCapacity(localProperties->getCount());
+ require_action(dict, exit, DEBUG_ERROR("No dictionary\n"));
+
+ 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));
+ }
+ }
+
+ result = dict->serialize(s);
+
+exit:
+ DEBUG_INFO("result=%d\n", result);
+ return result;
+}
+
+bool
+IODTNVRAM::init(IORegistryEntry *old, const IORegistryPlane *plane)
+{
+ OSSharedPtr<OSDictionary> 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;
+ }
+
+ // <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");
+
+ _nvramController = nvram;
+
+ // <rdar://problem/9529235> 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();
+ }
+
+ 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=0x%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);
+
+ 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<OSNumber> 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();
+}
+
+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 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);
+
+ 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::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;
+}
+
+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");
+ }
+
+ 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<OSDictionary> newDict;
+ OSSharedPtr<OSCollectionIterator> 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<OSDictionary> newDict;
+ OSSharedPtr<OSCollectionIterator> 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));
+ }
+ }
+
+ _commonDict = newDict;
+
+ DEBUG_INFO("common dictionary flushed\n");
+ }
+
+ special = true;
+ }
+
+exit:
+ if (error) {
+ *error = err;
+ }
+
+ return special;
+}
+
+OSSharedPtr<OSObject>
+IODTNVRAM::copyProperty(const OSSymbol *aKey) const
+{
+ IOReturn result;
+ const char *variableName;
+ uuid_t varGuid;
+ OSDictionary *dict;
+ OSSharedPtr<OSObject> theObject = nullptr;
+
+ if (aKey->isEqualTo(kIOBSDNameKey) ||
+ aKey->isEqualTo(kIOBSDNamesKey) ||
+ aKey->isEqualTo(kIOBSDMajorKey) ||
+ aKey->isEqualTo(kIOBSDMinorKey) ||
+ aKey->isEqualTo(kIOBSDUnitKey)) {
+ // These will never match.
+ // Check here and exit to avoid logging spam
+ return nullptr;
+ }
+ DEBUG_INFO("aKey=%s\n", aKey->getCStringNoCopy());
+
+ parseVariableName(aKey->getCStringNoCopy(), &varGuid, &variableName);
+
+ result = chooseDictionary(kIONVRAMOperationRead, &varGuid, variableName, &dict);
+ if (result != kIOReturnSuccess) {
+ goto exit;
+ }
+
+ if (!verifyPermission(kIONVRAMOperationRead, &varGuid, variableName)) {
+ DEBUG_INFO("Not privileged\n");
+ goto exit;
+ }
+
+ NVRAMLOCK();
+ theObject.reset(dict->getObject(variableName), OSRetain);
+ NVRAMUNLOCK();
+
+ if (theObject != nullptr) {
+ DEBUG_INFO("found data\n");
+ }
+
+exit:
+ return theObject;