You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
495 lines
14 KiB
495 lines
14 KiB
/*++
|
|
|
|
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;
|
|
PSYSTEM_POWER_STATE_DISABLE_REASON pReasonNoOSPM;
|
|
SYSTEM_POWER_LOGGING_ENTRY PowerLoggingEntry;
|
|
NTSTATUS ReasonStatus;
|
|
|
|
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;
|
|
}
|
|
} else {
|
|
//
|
|
// we're not registering an S3 handler because APM is not present.
|
|
// let the power manager know.
|
|
//
|
|
pReasonNoOSPM = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(SYSTEM_POWER_STATE_DISABLE_REASON),
|
|
HAL_POOL_TAG
|
|
);
|
|
if (pReasonNoOSPM) {
|
|
RtlZeroMemory(pReasonNoOSPM, sizeof(SYSTEM_POWER_STATE_DISABLE_REASON));
|
|
pReasonNoOSPM->AffectedState[PowerStateSleeping3] = TRUE;
|
|
pReasonNoOSPM->PowerReasonCode = SPSD_REASON_NOOSPM;
|
|
|
|
PowerLoggingEntry.LoggingType = LOGGING_TYPE_SPSD;
|
|
PowerLoggingEntry.LoggingEntry = pReasonNoOSPM;
|
|
|
|
ReasonStatus = ZwPowerInformation(
|
|
SystemPowerLoggingEntry,
|
|
&PowerLoggingEntry,
|
|
sizeof(PowerLoggingEntry),
|
|
NULL,
|
|
0 );
|
|
|
|
if (ReasonStatus != STATUS_SUCCESS) {
|
|
ExFreePool(pReasonNoOSPM);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
PSYSTEM_POWER_STATE_DISABLE_REASON pReasonBios;
|
|
SYSTEM_POWER_LOGGING_ENTRY PowerLoggingEntry;
|
|
NTSTATUS ReasonStatus;
|
|
|
|
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);
|
|
} else {
|
|
//
|
|
// we're not enabling hibernate because there is a hackflag
|
|
// that disallows hibernate. let the power manager know why.
|
|
//
|
|
pReasonBios = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(SYSTEM_POWER_STATE_DISABLE_REASON),
|
|
HAL_POOL_TAG
|
|
);
|
|
if (pReasonBios) {
|
|
RtlZeroMemory(pReasonBios, sizeof(SYSTEM_POWER_STATE_DISABLE_REASON));
|
|
pReasonBios->AffectedState[PowerStateSleeping4] = TRUE;
|
|
pReasonBios->PowerReasonCode = SPSD_REASON_BIOSINCOMPATIBLE;
|
|
|
|
PowerLoggingEntry.LoggingType = LOGGING_TYPE_SPSD;
|
|
PowerLoggingEntry.LoggingEntry = pReasonBios;
|
|
|
|
ReasonStatus = ZwPowerInformation(
|
|
SystemPowerLoggingEntry,
|
|
&PowerLoggingEntry,
|
|
sizeof(PowerLoggingEntry),
|
|
NULL,
|
|
0 );
|
|
|
|
if (ReasonStatus != STATUS_SUCCESS) {
|
|
ExFreePool(pReasonBios);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|