/*++ Copyright (c) 1997 Microsoft Corporation Module Name: ixhibrnt.c Abstract: This file provides the code that changes the system from the ACPI S0 (running) state to S4 (hibernated). Author: Jake Oshins (jakeo) May 6, 1997 Revision History: --*/ #include "halp.h" #include "ntapm.h" #include "ixsleep.h" NTSTATUS HalpRegisterPowerStateChange( PVOID ApmSleepVectorArg, PVOID ApmOffVectorArg ); NTSTATUS HaliLegacyPowerStateChange( IN PVOID Context, IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL, IN PVOID SystemContext, IN LONG NumberProcessors, IN volatile PLONG Number ); VOID HalpPowerStateCallbackApm( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 ); VOID (*ApmSleepVector)() = NULL; VOID (*ApmOffVector)() = NULL; extern BOOLEAN HalpDisableHibernate; #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, HaliInitPowerManagement) #pragma alloc_text(PAGE, HalpRegisterHibernate) #pragma alloc_text(PAGE, HalpPowerStateCallbackApm) #pragma alloc_text(PAGE, HalpRegisterPowerStateChange) #pragma alloc_text(PAGELK, HaliLegacyPowerStateChange) #pragma alloc_text(PAGELK, HalpSaveInterruptControllerState) #pragma alloc_text(PAGELK, HalpRestoreInterruptControllerState) #endif NTSTATUS HaliInitPowerManagement( IN PPM_DISPATCH_TABLE PmDriverDispatchTable, IN OUT PPM_DISPATCH_TABLE *PmHalDispatchTable ) { NTSTATUS status = STATUS_SUCCESS; PVOID ApmSleepVectorArg; PVOID ApmOffVectorArg; if (PmDriverDispatchTable->Signature != HAL_APM_SIGNATURE) { return STATUS_INVALID_PARAMETER; } if (PmDriverDispatchTable->Version != HAL_APM_VERSION) { return STATUS_INVALID_PARAMETER; } ApmSleepVectorArg = PmDriverDispatchTable->Function[HAL_APM_SLEEP_VECTOR]; ApmOffVectorArg = PmDriverDispatchTable->Function[HAL_APM_OFF_VECTOR]; status = HalpRegisterPowerStateChange( ApmSleepVectorArg, ApmOffVectorArg ); return status; } NTSTATUS HalpRegisterPowerStateChange( PVOID ApmSleepVectorArg, PVOID ApmOffVectorArg ) /*++ Routine Description: This function registers HaliLegacyPowerStateChange for the S3, S4, and OFF vectors. Arguments: PVOID ApmSleepVectorArg - pointer to a function which when called invokes the APM suspend/sleep function. PVOID ApmOffVectorArg - pointer to a function which when called invokes the APM code to shut off the machine. --*/ { POWER_STATE_HANDLER powerState; OBJECT_ATTRIBUTES objAttributes; PCALLBACK_OBJECT callback; UNICODE_STRING callbackName; NTSTATUS status; PAGED_CODE(); // // callbacks are set up for the hibernation case // at init, we just keep them. // // // Register the sleep3/suspend handler // if (ApmSleepVectorArg != NULL) { powerState.Type = PowerStateSleeping3; powerState.RtcWake = FALSE; powerState.Handler = &HaliLegacyPowerStateChange; powerState.Context = (PVOID)PowerStateSleeping3; status = ZwPowerInformation(SystemPowerStateHandler, &powerState, sizeof(POWER_STATE_HANDLER), NULL, 0); if (!NT_SUCCESS(status)) { return status; } } // // Register the OFF handler. // powerState.Type = PowerStateShutdownOff; powerState.RtcWake = FALSE; powerState.Handler = &HaliLegacyPowerStateChange; powerState.Context = (PVOID)PowerStateShutdownOff; status = ZwPowerInformation(SystemPowerStateHandler, &powerState, sizeof(POWER_STATE_HANDLER), NULL, 0); if (!NT_SUCCESS(status)) { // // n.b. We will return here with two vectors (sleep & hibernate) left in place. // return status; } ApmSleepVector = ApmSleepVectorArg; ApmOffVector = ApmOffVectorArg; return status; } VOID HalpRegisterHibernate( VOID ) /*++ Routine Description: This function registers a hibernation handler (for state S4) with the Policy Manager. Arguments: --*/ { POWER_STATE_HANDLER powerState; OBJECT_ATTRIBUTES objAttributes; PCALLBACK_OBJECT callback; UNICODE_STRING callbackName; PAGED_CODE(); // // Register callback that tells us to make // anything we need for sleeping non-pageable. // RtlInitUnicodeString(&callbackName, L"\\Callback\\PowerState"); InitializeObjectAttributes( &objAttributes, &callbackName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL ); ExCreateCallback(&callback, &objAttributes, FALSE, TRUE); ExRegisterCallback(callback, (PCALLBACK_FUNCTION)&HalpPowerStateCallbackApm, NULL); // // Register the hibernation handler. // if (HalpDisableHibernate == FALSE) { powerState.Type = PowerStateSleeping4; powerState.RtcWake = FALSE; powerState.Handler = &HaliLegacyPowerStateChange; powerState.Context = (PVOID)PowerStateSleeping4; ZwPowerInformation(SystemPowerStateHandler, &powerState, sizeof(POWER_STATE_HANDLER), NULL, 0); } return; } VOID HalpPowerStateCallbackApm( IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2 ) { ULONG action = (ULONG)Argument1; ULONG state = (ULONG)Argument2; if (action == PO_CB_SYSTEM_STATE_LOCK) { switch (state) { case 0: // lock down everything that can't page during sleep HalpSleepPageLock = MmLockPagableCodeSection((PVOID)HaliLegacyPowerStateChange); break; case 1: // unlock it all MmUnlockPagableImageSection(HalpSleepPageLock); } } } NTSTATUS HaliLegacyPowerStateChange( IN PVOID Context, IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL, IN PVOID SystemContext, IN LONG NumberProcessors, IN volatile PLONG Number ) /*++ Routine Description: This function calls out to code in a driver supplied wrapper function that will call off to APM to sleep==suspend, or power off (for either hibernate or system off) It is also called for hibernate when no driver supplied callout is available, in which case it makes the system ready so we can print a message and tell the user to manually power off the box. Arguments: --*/ { extern ULONG HalpProfilingStopped; NTSTATUS status = STATUS_SUCCESS; ASSERT( (Context == (PVOID)PowerStateSleeping3) || (Context == (PVOID)PowerStateSleeping4) || (Context == (PVOID)PowerStateShutdownOff)); ASSERT ( (ApmOffVector != NULL) || (SystemHandler != NULL) ); // // Save motherboard state. // HalpSaveInterruptControllerState(); HalpSaveDmaControllerState(); HalpSaveTimerState(); if (SystemHandler) { status = SystemHandler(SystemContext); // // System handler is present. If it return success, // then all out to APM bios // if ((status == STATUS_SUCCESS) || (status == STATUS_DEVICE_DOES_NOT_EXIST)) { if (Context == (PVOID)PowerStateSleeping3) { if (ApmSleepVector) { ApmSleepVector(); } else { // // this is expected path for odd operation, // caller will do something rational. // return STATUS_DEVICE_DOES_NOT_EXIST; } } else { // // The ApmOffVector provides the means to turn // off the machine. If the hibernation handler // returned STATUS_DEVICE_DOES_NOT_EXIST, however, // we don't want to turn the machine off, we want // to reset it. // if (ApmOffVector && !(status == STATUS_DEVICE_DOES_NOT_EXIST)) { // // This function should never return. The // machine should be off. But if this actually // does return, just fall through, as the return // code will cause the message to turn off the // machine to be displayed. // ApmOffVector(); } // // this is expected case for old non-apm machines, // caller will respond to this by putting up // message telling user to turn off the box. // (for either shutdown or hibernate) // return STATUS_DEVICE_DOES_NOT_EXIST; } } } else { // // there is no system handler, so just call out // to the bios // if (Context == (PVOID)PowerStateSleeping3) { if (ApmSleepVector) { ApmSleepVector(); } else { // // we're whistling in the wind here, we're // really probably hosed if this happens, but // this return is better than randomly puking. // return STATUS_DEVICE_DOES_NOT_EXIST; } } else { if (ApmOffVector) { ApmOffVector(); // // if we are right here, we have *returned* // from Off, which should never happen. // so report failure so the caller will tell the // user to turn the box off manually. // return STATUS_DEVICE_DOES_NOT_EXIST; } else { // // same as right above // return STATUS_DEVICE_DOES_NOT_EXIST; } } } // // Restore motherboard state. // HalpRestoreInterruptControllerState(); HalpRestoreDmaControllerState(); HalpRestoreTimerState(); if (HalpProfilingStopped == 0) { HalStartProfileInterrupt(0); } return status; } VOID HalpSaveInterruptControllerState( VOID ) { HalpSavePicState(); } VOID HalpRestoreInterruptControllerState( VOID ) { HalpRestorePicState(); }