windows-nt/Source/XPSP1/NT/base/hals/halx86/i386/ixslpsup.c

583 lines
14 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
ixslpsup.c
Abstract:
This file provides the code that saves and restores
state for traditional motherboard devices when the
system goes into a sleep state that removes power.
This code is included in multiple HALs.
Author:
Jake Oshins (jakeo) May 6, 1997
Revision History:
--*/
#include "halp.h"
#include "ixsleep.h"
#if defined(APIC_HAL)
#include "apic.inc"
#include "..\..\halmps\i386\pcmp_nt.inc"
VOID
StartPx_RMStub(
VOID
);
#endif
typedef struct _SAVE_CONTEXT_DPC_CONTEXT {
PVOID SaveArea;
volatile ULONG Complete;
} SAVE_CONTEXT_DPC_CONTEXT, *PSAVE_CONTEXT_DPC_CONTEXT;
VOID
HalpSaveContextTargetProcessor (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
#ifdef WANT_IRQ_ROUTING
#include "ixpciir.h"
#endif
extern UCHAR HalpAsmDataMarker;
extern PVOID HalpEisaControlBase;
extern ULONG HalpIrqMiniportInitialized;
PKPROCESSOR_STATE HalpHiberProcState;
ULONG CurTiledCr3LowPart;
PPHYSICAL_ADDRESS HalpTiledCr3Addresses;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, HaliLocateHiberRanges)
#pragma alloc_text(PAGELK, HalpSavePicState)
#pragma alloc_text(PAGELK, HalpSaveDmaControllerState)
#pragma alloc_text(PAGELK, HalpSaveTimerState)
#pragma alloc_text(PAGELK, HalpRestorePicState)
#pragma alloc_text(PAGELK, HalpRestoreDmaControllerState)
#pragma alloc_text(PAGELK, HalpRestoreTimerState)
#pragma alloc_text(PAGELK, HalpBuildResumeStructures)
#pragma alloc_text(PAGELK, HalpFreeResumeStructures)
#pragma alloc_text(PAGELK, HalpSaveContextTargetProcessor)
#endif
#define EISA_CONTROL (PUCHAR)&((PEISA_CONTROL) HalpEisaControlBase)
VOID
HalpPowerStateCallback(
IN PVOID CallbackContext,
IN PVOID Argument1,
IN PVOID Argument2
)
{
ULONG action = PtrToUlong(Argument1);
ULONG state = PtrToUlong(Argument2);
if (action == PO_CB_SYSTEM_STATE_LOCK) {
switch (state) {
case 0:
//
// Lock down everything in the PAGELK code section. (We chose
// HalpSaveDmaControllerState because it exists in every HAL.)
//
HalpSleepPageLock = MmLockPagableCodeSection((PVOID)HalpSaveDmaControllerState);
#if defined(APIC_HAL)
HalpSleepPage16Lock = MmLockPagableCodeSection((PVOID) StartPx_RMStub );
#endif
#ifdef ACPI_HAL
HalpMapNvsArea();
#endif
break;
case 1: // unlock it all
MmUnlockPagableImageSection(HalpSleepPageLock);
#ifdef APIC_HAL
MmUnlockPagableImageSection(HalpSleepPage16Lock);
#endif
#ifdef ACPI_HAL
HalpFreeNvsBuffers();
#endif
}
}
return;
}
VOID
HalpSavePicState(
VOID
)
{
HalpMotherboardState.PicState.MasterMask =
READ_PORT_UCHAR(EISA_CONTROL->Interrupt1ControlPort1);
HalpMotherboardState.PicState.SlaveMask =
READ_PORT_UCHAR(EISA_CONTROL->Interrupt2ControlPort1);
#if !defined(ACPI_HAL)
#ifdef WANT_IRQ_ROUTING
if(HalpIrqMiniportInitialized)
{
ULONG elcrMask = 0;
PciirqmpGetTrigger(&elcrMask);
HalpMotherboardState.PicState.MasterEdgeLevelControl = (UCHAR)((elcrMask >> 8) & 0xFF);
HalpMotherboardState.PicState.SlaveEdgeLevelControl = (UCHAR)(elcrMask & 0xFF);
}
else
{
#endif
if (HalpBusType == MACHINE_TYPE_EISA) {
#endif
HalpMotherboardState.PicState.MasterEdgeLevelControl =
READ_PORT_UCHAR(EISA_CONTROL->Interrupt1EdgeLevel);
HalpMotherboardState.PicState.SlaveEdgeLevelControl =
READ_PORT_UCHAR(EISA_CONTROL->Interrupt2EdgeLevel);
#if !defined(ACPI_HAL)
}
#ifdef WANT_IRQ_ROUTING
}
#endif
#endif
}
VOID
HalpRestorePicState(
VOID
)
{
ULONG flags;
flags = HalpDisableInterrupts();
HalpInitializePICs(FALSE);
WRITE_PORT_UCHAR(EISA_CONTROL->Interrupt1ControlPort1,
HalpMotherboardState.PicState.MasterMask);
WRITE_PORT_UCHAR(EISA_CONTROL->Interrupt2ControlPort1,
HalpMotherboardState.PicState.SlaveMask);
//
// For halx86, the PCI interrupt vector programming
// is static, so this code can just restore everything.
//
HalpRestorePicEdgeLevelRegister();
HalpRestoreInterrupts(flags);
}
VOID
HalpRestorePicEdgeLevelRegister(
VOID
)
{
#if !defined(ACPI_HAL)
#ifdef WANT_IRQ_ROUTING
if(HalpIrqMiniportInitialized)
{
PLINK_NODE linkNode;
PLINK_STATE temp;
ULONG elcrMask = (HalpMotherboardState.PicState.MasterEdgeLevelControl << 8) |
HalpMotherboardState.PicState.SlaveEdgeLevelControl;
PciirqmpSetTrigger(elcrMask);
//
// Reprogram all links.
//
for ( linkNode = HalpPciIrqRoutingInfo.LinkNodeHead;
linkNode;
linkNode = linkNode->Next)
{
//
// Swap the possible with the allocation.
//
temp = linkNode->Allocation;
linkNode->Allocation = linkNode->PossibleAllocation;
linkNode->PossibleAllocation = temp;
HalpCommitLink(linkNode);
}
}
else
{
#endif
if (HalpBusType == MACHINE_TYPE_EISA) {
#endif
WRITE_PORT_UCHAR(EISA_CONTROL->Interrupt1EdgeLevel,
HalpMotherboardState.PicState.MasterEdgeLevelControl);
WRITE_PORT_UCHAR(EISA_CONTROL->Interrupt2EdgeLevel,
HalpMotherboardState.PicState.SlaveEdgeLevelControl);
#if !defined(ACPI_HAL)
}
#ifdef WANT_IRQ_ROUTING
}
#endif
#endif
}
VOID
HalpSaveDmaControllerState(
VOID
)
{
}
VOID
HalpRestoreDmaControllerState(
VOID
)
/*++
Routine Description:
This function puts the DMA controller back into the
same state it was in before the machine went to sleep.
Arguments:
None.
Notes:
Normally, the DMA controller structures would be guarded
by spinlocks. But this function is called with interrupts
turned off and all but one processor spinning.
--*/
{
UCHAR i;
WRITE_PORT_UCHAR(EISA_CONTROL->Dma1BasePort.AllMask,0xF);
WRITE_PORT_UCHAR(EISA_CONTROL->Dma2BasePort.AllMask,0xE);
HalpIoDelay();
//
//Reset the DMA command registers
//
#if defined(NEC_98)
WRITE_PORT_UCHAR(EISA_CONTROL->Dma1BasePort.DmaStatus,0x40);
WRITE_PORT_UCHAR(EISA_CONTROL->Dma2BasePort.DmaStatus,0x40);
#else
WRITE_PORT_UCHAR(EISA_CONTROL->Dma1BasePort.DmaStatus,0);
WRITE_PORT_UCHAR(EISA_CONTROL->Dma2BasePort.DmaStatus,0);
#endif
HalpIoDelay();
for (i = 0; i < (EISA_DMA_CHANNELS / 2); i++) {
//
// Check to see if the array contains a value for this channel.
//
if (HalpDmaChannelState[i].ChannelProgrammed) {
WRITE_PORT_UCHAR(EISA_CONTROL->Dma1BasePort.Mode,
HalpDmaChannelState[i].ChannelMode);
if (HalpEisaDma) {
WRITE_PORT_UCHAR(EISA_CONTROL->Dma1ExtendedModePort,
HalpDmaChannelState[i].ChannelExtendedMode);
}
WRITE_PORT_UCHAR(EISA_CONTROL->Dma1BasePort.SingleMask,
HalpDmaChannelState[i].ChannelMask);
HalpIoDelay();
}
}
for (i = (EISA_DMA_CHANNELS / 2); i < EISA_DMA_CHANNELS; i++) {
//
// Check to see if the array contains a value for this channel.
//
if (HalpDmaChannelState[i].ChannelProgrammed) {
WRITE_PORT_UCHAR(EISA_CONTROL->Dma2BasePort.Mode,
HalpDmaChannelState[i].ChannelMode);
if (HalpEisaDma) {
WRITE_PORT_UCHAR(EISA_CONTROL->Dma2ExtendedModePort,
HalpDmaChannelState[i].ChannelExtendedMode);
}
WRITE_PORT_UCHAR(EISA_CONTROL->Dma2BasePort.SingleMask,
HalpDmaChannelState[i].ChannelMask);
HalpIoDelay();
}
}
}
VOID
HalpSaveTimerState(
VOID
)
{
}
VOID
HalpRestoreTimerState(
VOID
)
{
HalpInitializeClock();
}
VOID
HaliLocateHiberRanges (
IN PVOID MemoryMap
)
{
//
// Mark the hal's data section as needed to be cloned
//
PoSetHiberRange (
MemoryMap,
PO_MEM_CLONE,
(PVOID) &HalpFeatureBits,
0,
'dlah'
);
#if defined(_HALPAE_)
//
// Mark DMA buffers as not needing to be saved.
//
if (MasterAdapter24.MapBufferSize != 0) {
PoSetHiberRange( MemoryMap,
PO_MEM_DISCARD | PO_MEM_PAGE_ADDRESS,
(PVOID)(ULONG_PTR)(MasterAdapter24.MapBufferPhysicalAddress.LowPart >>
PAGE_SHIFT),
MasterAdapter24.MapBufferSize >> PAGE_SHIFT,
'mlah' );
}
if (MasterAdapter32.MapBufferSize != 0) {
PoSetHiberRange( MemoryMap,
PO_MEM_DISCARD | PO_MEM_PAGE_ADDRESS,
(PVOID)(ULONG_PTR)(MasterAdapter32.MapBufferPhysicalAddress.LowPart >>
PAGE_SHIFT),
MasterAdapter32.MapBufferSize >> PAGE_SHIFT,
'mlah' );
}
#else
//
// Mark DMA buffer has not needing saved
//
if (HalpMapBufferSize) {
PoSetHiberRange (
MemoryMap,
PO_MEM_DISCARD | PO_MEM_PAGE_ADDRESS,
(PVOID) (HalpMapBufferPhysicalAddress.LowPart >> PAGE_SHIFT),
HalpMapBufferSize >> PAGE_SHIFT,
'mlah'
);
}
#endif
}
NTSTATUS
HalpBuildResumeStructures(
VOID
)
{
KAFFINITY CurrentAffinity, ActiveProcessors;
ULONG ProcNum, Processor, NumberProcessors = 1;
KDPC Dpc;
SAVE_CONTEXT_DPC_CONTEXT Context;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
#if defined(APIC_HAL)
//
// If KeActiveProcessors() were callable at
// DISPATCH_LEVEL, I would use that.
//
NumberProcessors = HalpMpInfoTable.NtProcessors;
#endif
ActiveProcessors = (1 << NumberProcessors) - 1;
#if defined(APIC_HAL) || defined(ACPI_HAL)
//
// Allocate space to save processor context for other processors
//
HalpTiledCr3Addresses = NULL;
HalpHiberProcState =
ExAllocatePoolWithTag(NonPagedPool,
(NumberProcessors * sizeof(KPROCESSOR_STATE)),
HAL_POOL_TAG);
if (!HalpHiberProcState) {
goto BuildResumeStructuresError;
}
RtlZeroMemory(HalpHiberProcState,
NumberProcessors * sizeof(KPROCESSOR_STATE));
//
// Allocate space for tiled CR3 for all processors
//
HalpTiledCr3Addresses =
ExAllocatePoolWithTag(NonPagedPool,
(NumberProcessors * sizeof(PHYSICAL_ADDRESS)),
HAL_POOL_TAG);
if (!HalpTiledCr3Addresses) {
goto BuildResumeStructuresError;
}
RtlZeroMemory(HalpTiledCr3Addresses,
(NumberProcessors * sizeof(PHYSICAL_ADDRESS)));
//
// Get IDT and GDT for all processors except BSP,
// map and save tiled CR3
//
KeInitializeDpc (&Dpc, HalpSaveContextTargetProcessor, &Context);
KeSetImportanceDpc (&Dpc, HighImportance);
ProcNum = 0;
CurrentAffinity = 1;
Processor = 0;
while (ActiveProcessors) {
if (ActiveProcessors & CurrentAffinity) {
ActiveProcessors &= ~CurrentAffinity;
RtlZeroMemory(&Context, sizeof(Context));
Context.SaveArea = &(HalpHiberProcState[ProcNum]);
if (Processor == (KeGetPcr())->Prcb->Number) {
//
// We're running on this processor. Just call
// the DPC routine from here.
HalpSaveContextTargetProcessor(&Dpc, &Context, NULL, NULL);
} else {
//
// Issue DPC to target processor
//
KeSetTargetProcessorDpc (&Dpc, (CCHAR) Processor);
KeInsertQueueDpc (&Dpc, NULL, NULL);
//
// Wait for DPC to be complete.
//
while (Context.Complete == FALSE);
}
ProcNum++;
}
Processor++;
CurrentAffinity <<= 1;
}
for (ProcNum = 0; ProcNum < NumberProcessors; ProcNum++) {
HalpTiledCr3Addresses[ProcNum].LowPart =
HalpBuildTiledCR3Ex(&(HalpHiberProcState[ProcNum]),ProcNum);
}
#endif
return STATUS_SUCCESS;
#if defined(APIC_HAL) || defined(ACPI_HAL)
BuildResumeStructuresError:
if (HalpHiberProcState) ExFreePool(HalpHiberProcState);
if (HalpTiledCr3Addresses) ExFreePool(HalpTiledCr3Addresses);
return STATUS_UNSUCCESSFUL;
#endif
}
NTSTATUS
HalpFreeResumeStructures(
VOID
)
{
ULONG ProcNum, NumberProcessors = 1;
#if defined(APIC_HAL)
NumberProcessors = HalpMpInfoTable.NtProcessors;
#endif
#if defined(APIC_HAL) || defined(ACPI_HAL)
if (HalpHiberProcState) {
ExFreePool(HalpHiberProcState);
HalpHiberProcState = NULL;
}
if (HalpTiledCr3Addresses) {
ExFreePool(HalpTiledCr3Addresses);
HalpTiledCr3Addresses = NULL;
}
for (ProcNum = 0; ProcNum < NumberProcessors; ProcNum++) {
HalpFreeTiledCR3Ex(ProcNum);
}
#endif
return STATUS_SUCCESS;
}
VOID
HalpSaveContextTargetProcessor (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PSAVE_CONTEXT_DPC_CONTEXT Context = (PSAVE_CONTEXT_DPC_CONTEXT)DeferredContext;
KeSaveStateForHibernate(Context->SaveArea);
InterlockedIncrement(&Context->Complete);
}