Loading...
iokit/Kernel/IONVRAMV3Handler.cpp xnu-12377.121.6 xnu-12377.1.9
--- xnu/xnu-12377.121.6/iokit/Kernel/IONVRAMV3Handler.cpp
+++ xnu/xnu-12377.1.9/iokit/Kernel/IONVRAMV3Handler.cpp
@@ -232,6 +232,8 @@
 	void findExistingEntry(const uuid_t varGuid, const char *varName, struct nvram_v3_var_entry **existing, unsigned int *existingIndex);
 	IOReturn syncRaw(void);
 	IOReturn syncBlock(void);
+	IOReturn handleEphDM(void);
+
 public:
 	virtual
 	~IONVRAMV3Handler() APPLE_KEXT_OVERRIDE;
@@ -414,7 +416,7 @@
 
 	_currentBank = controllerBank;
 
-	controllerImage = (uint8_t *)IOMallocZeroData(_bankSize);
+	controllerImage = (uint8_t *)IOMallocData(_bankSize);
 
 	_nvramController->select(_currentBank);
 	_nvramController->read(0, controllerImage, _bankSize);
@@ -549,7 +551,8 @@
 		if (_provider->_diags) {
 			_provider->_diags->logVariable(getPartitionTypeForGUID(v3Entry->header.guid),
 			    kIONVRAMOperationDelete,
-			    variableName);
+			    variableName,
+			    nullptr);
 		}
 	}
 
@@ -635,6 +638,79 @@
 	return ret;
 }
 
+typedef struct {
+	const char            *name;
+	OSSharedPtr<OSObject> value;
+} ephDMAllowListEntry;
+
+static
+ephDMAllowListEntry ephDMEntries[] = {
+	// Mobile Obliteration clears the following variables after it runs
+	{ .name = "oblit-begins" },
+	{ .name = "orig-oblit" },
+	{ .name = "oblit-failure" },
+	{ .name = "oblit-inprogress" },
+	{ .name = "obliteration" },
+	// darwin-init is used for configuring internal builds
+	{ .name = "darwin-init" }
+};
+
+IOReturn
+IONVRAMV3Handler::handleEphDM(void)
+{
+	OSSharedPtr<IORegistryEntry> entry;
+	OSData*                      data;
+	OSSharedPtr<OSObject>        prop;
+	uint32_t                     ephDM = 0;
+	IOReturn                     ret = kIOReturnSuccess;
+	OSSharedPtr<const OSSymbol>  canonicalKey;
+	uint32_t                     skip = 0;
+
+	// For ephemeral data mode, NVRAM needs to be cleared on every boot
+	// For system region supported targets, iBoot clears the system region
+	// For other targets, iBoot clears all the persistent variables
+	// So xnu only needs to clear the common region
+	entry = IORegistryEntry::fromPath("/product", gIODTPlane);
+	if (entry) {
+		prop = entry->copyProperty("ephemeral-data-mode");
+		if (prop) {
+			data = OSDynamicCast(OSData, prop.get());
+			if (data) {
+				ephDM = *((uint32_t *)data->getBytesNoCopy());
+			}
+		}
+	}
+
+	require_action(ephDM != 0, exit, DEBUG_ALWAYS("ephemeral-data-mode not supported\n"));
+	require_action(_systemSize != 0, exit, DEBUG_ALWAYS("No system region, no need to clear\n"));
+
+	if (PE_parse_boot_argn("epdm-skip-nvram", &skip, sizeof(skip))) {
+		require_action(!(gInternalBuild && (skip == 1)), exit, DEBUG_ALWAYS("Internal build + epdm-skip-nvram set to true, skip nvram clearing\n"));
+	}
+
+	// Go through the allowlist and stash the values
+	for (uint32_t entry = 0; entry < ARRAY_SIZE(ephDMEntries); entry++) {
+		canonicalKey = keyWithGuidAndCString(gAppleNVRAMGuid, ephDMEntries[entry].name);
+		ephDMEntries[entry].value.reset(OSDynamicCast(OSData, _varDict->getObject(canonicalKey.get())), OSRetain);
+	}
+
+	DEBUG_ALWAYS("Obliterating common region\n");
+	ret = flush(gAppleNVRAMGuid, kIONVRAMOperationObliterate);
+	require_noerr_action(ret, exit, DEBUG_ERROR("Flushing common region failed, ret=%#08x\n", ret));
+
+	// Now write the allowlist variables back
+	for (uint32_t entry = 0; entry < ARRAY_SIZE(ephDMEntries); entry++) {
+		if (ephDMEntries[entry].value.get() == nullptr) {
+			continue;
+		}
+		ret = setVariableInternal(gAppleNVRAMGuid, ephDMEntries[entry].name, ephDMEntries[entry].value.get());
+		require_noerr_action(ret, exit, DEBUG_ERROR("Setting allowlist variable %s failed, ret=%#08x\n", ephDMEntries[entry].name, ret));
+	}
+
+exit:
+	return ret;
+}
+
 IOReturn
 IONVRAMV3Handler::unserializeVariables(void)
 {
@@ -732,16 +808,15 @@
 		    propSymbol, propObject)) {
 			OSSharedPtr<const OSSymbol> canonicalKey = keyWithGuidAndCString(v3Entry->header.guid, (const char *)v3Entry->header.name_data_buf);
 
-			DEBUG_INFO("adding %s, variableLength=%zu, dataLength=%u, system=%d\n",
-			    canonicalKey->getCStringNoCopy(), variable_length(header), v3Entry->header.dataSize, system);
+			DEBUG_INFO("adding %s, dataLength=%u, system=%d\n",
+			    canonicalKey->getCStringNoCopy(), v3Entry->header.dataSize, system);
 
 			_varDict->setObject(canonicalKey.get(), propObject.get());
 
 			if (_provider->_diags) {
 				_provider->_diags->logVariable(getPartitionTypeForGUID(v3Entry->header.guid),
 				    kIONVRAMOperationInit, propSymbol.get()->getCStringNoCopy(),
-				    (void *)(uintptr_t)v3Entry->header.dataSize,
-				    (void *)(uintptr_t)offset);
+				    (void *)(uintptr_t)(header->name_data_buf + header->nameSize));
 			}
 		}
 		IOFreeData(v3Entry, nvram_v3_var_container_size(header));
@@ -995,7 +1070,7 @@
 	DEBUG_INFO("called\n");
 	NVRAMLOCKASSERTHELD(_controllerLock);
 
-	bankData = (uint8_t *)IOMallocZeroData(_bankSize);
+	bankData = (uint8_t *)IOMallocData(_bankSize);
 	require_action(bankData != nullptr, exit, ret = kIOReturnNoMemory);
 
 	ret = _nvramController->select(next_bank);
@@ -1096,6 +1171,7 @@
 	size_t                    *invalidateOffsets = nullptr;
 	size_t                    invalidateOffsetsCount = 0;
 	size_t                    invalidateOffsetIndex = 0;
+	size_t                    invalidatedSize = 0;
 
 	require_action(_nvramController != nullptr, exit, DEBUG_INFO("No _nvramController\n"));
 	require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n"));
@@ -1108,11 +1184,11 @@
 		// No reclaim, build append and invalidate list
 		remainingEntries = OSArray::withCapacity(_varEntries->getCapacity());
 
-		appendBuffer = (uint8_t *)IOMallocZeroData(_bankSize);
+		appendBuffer = (uint8_t *)IOMallocData(_bankSize);
 		require_action(appendBuffer, unlock, ret = kIOReturnNoMemory);
 
 		invalidateOffsetsCount = _varEntries->getCount();
-		invalidateOffsets = (size_t *)IOMallocZeroData(invalidateOffsetsCount * sizeof(size_t));
+		invalidateOffsets = (size_t *)IOMallocData(invalidateOffsetsCount * sizeof(size_t));
 		require_action(invalidateOffsets, unlock, ret = kIOReturnNoMemory);
 
 		for (unsigned int i = 0; i < _varEntries->getCount(); i++) {
@@ -1140,13 +1216,16 @@
 
 				if (prevOffset) {
 					invalidateOffsets[invalidateOffsetIndex++] = prevOffset;
+					invalidatedSize += variable_length((struct v3_var_header *)prevOffset);
 				}
 
 				remainingEntries->setObject(entryContainer);
 			} else if (varEntry->new_state == VAR_NEW_STATE_REMOVE) {
 				if (varEntry->existing_offset) {
 					DEBUG_INFO("marking entry at offset %#lx deleted\n", varEntry->existing_offset);
+
 					invalidateOffsets[invalidateOffsetIndex++] = varEntry->existing_offset;
+					invalidatedSize += variable_length((struct v3_var_header *)varEntry->existing_offset);
 				} else {
 					DEBUG_INFO("No existing_offset , removing\n");
 				}
@@ -1218,7 +1297,7 @@
 	require_action(_newData == true, exit, DEBUG_INFO("No _newData to sync\n"));
 	require_action(_bankSize != 0, exit, DEBUG_INFO("No nvram size info\n"));
 
-	block = (uint8_t *)IOMallocZeroData(_bankSize);
+	block = (uint8_t *)IOMallocData(_bankSize);
 
 	NVRAMREADLOCK(_variableLock);
 	remainingEntries = OSArray::withCapacity(_varEntries->getCapacity());
@@ -1261,10 +1340,6 @@
 			DEBUG_INFO("Dropping %s\n", varEntry->header.name_data_buf);
 		}
 	}
-
-	// 0xFF out the remaining space, this will allow banks to switch between append mode and
-	// block mode if ever needed
-	memset(block + new_bank_offset, 0xFF, _bankSize - (uint32_t)new_bank_offset);
 
 	ret = _nvramController->write(0, block, _bankSize);
 	verify_noerr_action(ret, DEBUG_ERROR("w fail, ret=%#x\n", ret));