+#if defined(__i386__) || defined(__x86_64__)
+ IOService::getPMRootDomain()->removeProperty(gIOHibernateRTCVariablesKey);
+ IOService::getPMRootDomain()->removeProperty(kIOHibernateSMCVariablesKey);
+
+ /*
+ * Hibernate variable is written to NVRAM on platforms in which RtcRam
+ * is not backed by coin cell. Remove Hibernate data from NVRAM.
+ */
+ if (gIOOptionsEntry) {
+ if (gIOHibernateRTCVariablesKey) {
+ if (gIOOptionsEntry->getProperty(gIOHibernateRTCVariablesKey)) {
+ gIOOptionsEntry->removeProperty(gIOHibernateRTCVariablesKey);
+ }
+ }
+
+ if (gIOHibernateBootNextKey) {
+ if (gIOHibernateBootNextSave) {
+ gIOOptionsEntry->setProperty(gIOHibernateBootNextKey, gIOHibernateBootNextSave);
+ gIOHibernateBootNextSave->release();
+ gIOHibernateBootNextSave = NULL;
+ } else {
+ gIOOptionsEntry->removeProperty(gIOHibernateBootNextKey);
+ }
+ }
+ if (kIOHibernateStateWakingFromHibernate != gIOHibernateState) {
+ gIOOptionsEntry->sync();
+ }
+ }
+#endif
+
+ if (vars->srcBuffer) {
+ vars->srcBuffer->release();
+ }
+ bzero(&gIOHibernateHandoffPages[0], gIOHibernateHandoffPageCount * sizeof(gIOHibernateHandoffPages[0]));
+ if (vars->handoffBuffer) {
+ if (kIOHibernateStateWakingFromHibernate == gIOHibernateState) {
+ IOHibernateHandoff * handoff;
+ bool done = false;
+ for (handoff = (IOHibernateHandoff *) vars->handoffBuffer->getBytesNoCopy();
+ !done;
+ handoff = (IOHibernateHandoff *) &handoff->data[handoff->bytecount]) {
+ HIBPRINT("handoff %p, %x, %x\n", handoff, handoff->type, handoff->bytecount);
+ uint8_t * data = &handoff->data[0];
+ switch (handoff->type) {
+ case kIOHibernateHandoffTypeEnd:
+ done = true;
+ break;
+
+ case kIOHibernateHandoffTypeDeviceTree:
+ MergeDeviceTree((DeviceTreeNode *) data, IOService::getServiceRoot());
+ break;
+
+ case kIOHibernateHandoffTypeKeyStore:
+#if defined(__i386__) || defined(__x86_64__)
+ {
+ IOBufferMemoryDescriptor *
+ md = IOBufferMemoryDescriptor::withBytes(data, handoff->bytecount, kIODirectionOutIn);
+ if (md) {
+ IOSetKeyStoreData(md);
+ }
+ }
+#endif
+ break;
+
+ default:
+ done = (kIOHibernateHandoffType != (handoff->type & 0xFFFF0000));
+ break;
+ }
+ }
+#if defined(__i386__) || defined(__x86_64__)
+ if (vars->volumeCryptKeySize) {
+ IOBufferMemoryDescriptor *
+ bmd = IOBufferMemoryDescriptor::withBytes(&vars->volumeCryptKey[0],
+ vars->volumeCryptKeySize, kIODirectionOutIn);
+ if (!bmd) {
+ panic("IOBufferMemoryDescriptor");
+ }
+ IOSetAPFSKeyStoreData(bmd);
+ bzero(&vars->volumeCryptKey[0], sizeof(vars->volumeCryptKey));
+ }
+#endif
+ }
+ vars->handoffBuffer->release();
+ }
+
+ if (gIOChosenEntry
+ && (data = OSDynamicCast(OSData, gIOChosenEntry->getProperty(gIOBridgeBootSessionUUIDKey)))
+ && (sizeof(gIOHibernateBridgeBootSessionUUIDString) <= data->getLength())) {
+ bcopy(data->getBytesNoCopy(), &gIOHibernateBridgeBootSessionUUIDString[0],
+ sizeof(gIOHibernateBridgeBootSessionUUIDString));
+ }
+
+ if (vars->hwEncrypt) {
+ err = IOPolledFilePollersSetEncryptionKey(vars->fileVars, NULL, 0);
+ HIBLOG("IOPolledFilePollersSetEncryptionKey(0,%x)\n", err);
+ }
+
+ bzero(vars, sizeof(*vars));
+
+// gIOHibernateState = kIOHibernateStateInactive; // leave it for post wake code to see
+
+ return kIOReturnSuccess;
+}
+
+static void
+IOHibernateSystemPostWakeTrim(void * p1, void * p2)
+{
+ // invalidate & close the image file
+ if (p1) {
+ IOLockLock(gFSLock);
+ }
+ if (kFSTrimDelay == gFSState) {
+ IOPolledFileIOVars * vars = &gFileVars;
+ IOPolledFileClose(&vars,
+#if DISABLE_TRIM
+ 0, NULL, 0, 0, 0);
+#else
+ 0, (caddr_t)gIOHibernateCurrentHeader, sizeof(IOHibernateImageHeader),
+ sizeof(IOHibernateImageHeader), gIOHibernateCurrentHeader->imageSize);
+#endif
+ gFSState = kFSIdle;
+ }
+ if (p1) {
+ IOLockUnlock(gFSLock);
+ }
+}
+
+IOReturn
+IOHibernateSystemPostWake(bool now)
+{
+ gIOHibernateCurrentHeader->signature = kIOHibernateHeaderInvalidSignature;
+ IOSetBootImageNVRAM(NULL);
+
+ IOLockLock(gFSLock);
+ if (kFSTrimDelay == gFSState) {
+ thread_call_cancel(gIOHibernateTrimCalloutEntry);
+ IOHibernateSystemPostWakeTrim(NULL, NULL);
+ } else if (kFSOpened != gFSState) {
+ gFSState = kFSIdle;
+ } else {
+ gFSState = kFSTrimDelay;
+ if (now) {
+ thread_call_cancel(gIOHibernateTrimCalloutEntry);
+ IOHibernateSystemPostWakeTrim(NULL, NULL);
+ } else {
+ AbsoluteTime deadline;
+ clock_interval_to_deadline(TRIM_DELAY, kMillisecondScale, &deadline );
+ thread_call_enter1_delayed(gIOHibernateTrimCalloutEntry, NULL, deadline);
+ }
+ }
+ IOLockUnlock(gFSLock);
+
+ return kIOReturnSuccess;
+}
+
+uint32_t
+IOHibernateWasScreenLocked(void)
+{
+ uint32_t ret = 0;
+ if ((kIOHibernateStateWakingFromHibernate == gIOHibernateState) && gIOChosenEntry) {
+ OSData *
+ data = OSDynamicCast(OSData, gIOChosenEntry->getProperty(kIOScreenLockStateKey));
+ if (data) {
+ ret = ((uint32_t *)data->getBytesNoCopy())[0];
+ gIOChosenEntry->setProperty(kIOBooterScreenLockStateKey, data);
+ }
+ } else {
+ gIOChosenEntry->removeProperty(kIOBooterScreenLockStateKey);
+ }
+
+ return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+SYSCTL_STRING(_kern, OID_AUTO, hibernatefile,
+ CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+ gIOHibernateFilename, sizeof(gIOHibernateFilename), "");
+SYSCTL_STRING(_kern, OID_AUTO, bootsignature,
+ CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+ gIOHibernateBootSignature, sizeof(gIOHibernateBootSignature), "");
+SYSCTL_UINT(_kern, OID_AUTO, hibernatemode,
+ CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+ &gIOHibernateMode, 0, "");
+SYSCTL_STRUCT(_kern, OID_AUTO, hibernatestatistics,
+ CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+ &_hibernateStats, hibernate_statistics_t, "");
+SYSCTL_STRING(_kern_bridge, OID_AUTO, bootsessionuuid,
+ CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
+ gIOHibernateBridgeBootSessionUUIDString, sizeof(gIOHibernateBridgeBootSessionUUIDString), "");
+
+SYSCTL_UINT(_kern, OID_AUTO, hibernategraphicsready,
+ CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_ANYBODY,
+ &_hibernateStats.graphicsReadyTime, 0, "");
+SYSCTL_UINT(_kern, OID_AUTO, hibernatewakenotification,
+ CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_ANYBODY,
+ &_hibernateStats.wakeNotificationTime, 0, "");
+SYSCTL_UINT(_kern, OID_AUTO, hibernatelockscreenready,
+ CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_ANYBODY,
+ &_hibernateStats.lockScreenReadyTime, 0, "");
+SYSCTL_UINT(_kern, OID_AUTO, hibernatehidready,
+ CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_ANYBODY,
+ &_hibernateStats.hidReadyTime, 0, "");
+
+void
+IOHibernateSystemInit(IOPMrootDomain * rootDomain)
+{
+ gIOHibernateBootImageKey = OSSymbol::withCStringNoCopy(kIOHibernateBootImageKey);
+ gIOHibernateBootSignatureKey = OSSymbol::withCStringNoCopy(kIOHibernateBootSignatureKey);
+ gIOBridgeBootSessionUUIDKey = OSSymbol::withCStringNoCopy(kIOBridgeBootSessionUUIDKey);
+
+#if defined(__i386__) || defined(__x86_64__)
+ gIOHibernateRTCVariablesKey = OSSymbol::withCStringNoCopy(kIOHibernateRTCVariablesKey);
+ gIOHibernateBoot0082Key = OSSymbol::withCString("8BE4DF61-93CA-11D2-AA0D-00E098032B8C:Boot0082");
+ gIOHibernateBootNextKey = OSSymbol::withCString("8BE4DF61-93CA-11D2-AA0D-00E098032B8C:BootNext");
+ gIOHibernateRTCVariablesKey = OSSymbol::withCStringNoCopy(kIOHibernateRTCVariablesKey);
+#endif /* defined(__i386__) || defined(__x86_64__) */
+
+ OSData * data = OSData::withBytesNoCopy(&gIOHibernateState, sizeof(gIOHibernateState));
+ if (data) {
+ rootDomain->setProperty(kIOHibernateStateKey, data);
+ data->release();
+ }
+
+ if (PE_parse_boot_argn("hfile", gIOHibernateFilename, sizeof(gIOHibernateFilename))) {
+ gIOHibernateMode = kIOHibernateModeOn;
+ } else {
+ gIOHibernateFilename[0] = 0;
+ }
+
+ sysctl_register_oid(&sysctl__kern_hibernatefile);
+ sysctl_register_oid(&sysctl__kern_bootsignature);
+ sysctl_register_oid(&sysctl__kern_hibernatemode);
+ sysctl_register_oid(&sysctl__kern_hibernatestatistics);
+ sysctl_register_oid(&sysctl__kern_hibernategraphicsready);
+ sysctl_register_oid(&sysctl__kern_hibernatewakenotification);
+ sysctl_register_oid(&sysctl__kern_hibernatelockscreenready);
+ sysctl_register_oid(&sysctl__kern_hibernatehidready);
+
+ gIOChosenEntry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
+
+ if (gIOChosenEntry
+ && (data = OSDynamicCast(OSData, gIOChosenEntry->getProperty(gIOBridgeBootSessionUUIDKey)))
+ && (sizeof(gIOHibernateBridgeBootSessionUUIDString) <= data->getLength())) {
+ sysctl_register_oid(&sysctl__kern_bridge_bootsessionuuid);
+ bcopy(data->getBytesNoCopy(), &gIOHibernateBridgeBootSessionUUIDString[0], sizeof(gIOHibernateBridgeBootSessionUUIDString));
+ }
+
+ gFSLock = IOLockAlloc();
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static IOReturn
+IOHibernatePolledFileWrite(IOPolledFileIOVars * vars,
+ const uint8_t * bytes, IOByteCount size,
+ IOPolledFileCryptVars * cryptvars)
+{
+ IOReturn err;
+
+ err = IOPolledFileWrite(vars, bytes, size, cryptvars);
+ if ((kIOReturnSuccess == err) && hibernate_should_abort()) {
+ err = kIOReturnAborted;
+ }
+
+ return err;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */