Leaked source code of windows server 2003
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.
 
 
 
 
 
 

729 lines
17 KiB

/*++
Copyright (c) 1990, 1991 Microsoft Corporation
Module Name:
wake.c
Abstract:
Author:
Ken Reneris
Environment:
Kernel Mode
Revision History:
Steve Deng (sdeng) 20-Aug-2002
Add support for PAE and Amd64. It is assumed that all the
physical pages are below 4GB. Otherwise hibernation feature
should be disabled.
--*/
#include "arccodes.h"
#include "bootx86.h"
extern PHARDWARE_PTE PDE;
extern PHARDWARE_PTE HalPT;
extern ULONG HiberNoMappings;
extern BOOLEAN HiberIoError;
extern ULONG HiberLastRemap;
extern BOOLEAN HiberOutOfRemap;
extern UCHAR BlpEnablePAEStart;
extern UCHAR BlpEnablePAEEnd;
extern UCHAR BlAmd64SwitchToLongModeStart;
extern UCHAR BlAmd64SwitchToLongModeEnd;
PVOID HiberTransVa;
ULONG64 HiberTransVaAmd64;
ULONG HiberCurrentMapIndex;
#define PDE_SHIFT 22
#define PTE_SHIFT 12
#define PTE_INDEX_MASK 0x3ff
#define PDPT_SHIFT_X86PAE 30
#define PDE_SHIFT_X86PAE 21
#define PTE_SHIFT_X86PAE 12
#define PDE_INDEX_MASK_X86PAE 0x1ff
#define PTE_INDEX_MASK_X86PAE 0x1ff
VOID
HiberSetupForWakeDispatchPAE (
VOID
);
VOID
HiberSetupForWakeDispatchX86 (
VOID
);
VOID
HiberSetupForWakeDispatchAmd64 (
VOID
);
PVOID
HbMapPte (
IN ULONG PteToMap,
IN ULONG Page
)
{
PVOID Va;
Va = (PVOID) (HiberVa + (PteToMap << PAGE_SHIFT));
HbSetPte (Va, HiberPtes, PteToMap, Page);
return Va;
}
PVOID
HbNextSharedPage (
IN ULONG PteToMap,
IN ULONG RealPage
)
/*++
Routine Description:
Allocates the next available page in the free and
maps the Hiber pte to the page. The allocated page
is put onto the remap list
Arguments:
PteToMap - Which Hiber PTE to map
RealPage - The page to enter into the remap table for
this allocation
Return Value:
Virtual address of the mapping
--*/
{
PULONG MapPage;
PULONG RemapPage;
ULONG DestPage;
ULONG i;
MapPage = (PULONG) (HiberVa + (PTE_MAP_PAGE << PAGE_SHIFT));
RemapPage = (PULONG) (HiberVa + (PTE_REMAP_PAGE << PAGE_SHIFT));
//
// Loop until we find a free page which is not in
// use by the loader image, then map it
//
while (HiberCurrentMapIndex < HiberNoMappings) {
DestPage = MapPage[HiberCurrentMapIndex];
HiberCurrentMapIndex += 1;
i = HbPageDisposition (DestPage);
if (i == HbPageInvalid) {
HiberIoError = TRUE;
return HiberBuffer;
}
if (i == HbPageNotInUse) {
MapPage[HiberLastRemap] = DestPage;
RemapPage[HiberLastRemap] = RealPage;
HiberLastRemap += 1;
HiberPageFrames[PteToMap] = DestPage;
return HbMapPte(PteToMap, DestPage);
}
}
HiberOutOfRemap = TRUE;
return HiberBuffer;
}
VOID
HbAllocatePtes (
IN ULONG NumberPages,
OUT PVOID *PteAddress,
OUT PVOID *MappedAddress
)
/*++
Routine Description:
Allocated a consecutive chuck of Ptes.
Arguments:
NumberPage - Number of ptes to allocate
PteAddress - Pointer to the first PTE
MappedAddress - Base VA of the address mapped
--*/
{
ULONG i;
ULONG j;
//
// We use the HAL's PDE for mapping. Find enough free PTEs
//
for (i=0; i<=1024-NumberPages; i++) {
for (j=0; j < NumberPages; j++) {
if ((((PULONG)HalPT))[i+j]) {
break;
}
}
if (j == NumberPages) {
*PteAddress = (PVOID) &HalPT[i];
*MappedAddress = (PVOID) (0xffc00000 | (i<<12));
return ;
}
i += j;
}
BlPrint("NoMem");
while (1);
}
VOID
HbSetPte (
IN PVOID Va,
IN PHARDWARE_PTE Pte,
IN ULONG Index,
IN ULONG PageNumber
)
/*++
Routine Description:
Sets the Pte to the corresponding page address
Arguments:
Va - the virtual address of the physical page described by PageNumber
Pte - the base address of Page Table
Index - the index into Page Table
PageNumber - the page frame number of the pysical page to be mapped
Return:
None
--*/
{
Pte[Index].PageFrameNumber = PageNumber;
Pte[Index].Valid = 1;
Pte[Index].Write = 1;
Pte[Index].WriteThrough = 0;
Pte[Index].CacheDisable = 0;
_asm {
mov eax, Va
invlpg [eax]
}
}
VOID
HbMapPagePAE(
PHARDWARE_PTE_X86PAE HbPdpt,
PVOID VirtualAddress,
ULONG PageFrameNumber
)
/*++
Routine Description:
This functions maps a virtural address into PAE page mapping structure.
Arguments:
HbPdpt - Supplies the base address of Page Directory Pointer Table.
VirtualAddress - Supplies the virtual address to map
PageFrameNumber - Supplies the physical page number to map the address to
Return:
None
--*/
{
ULONG index;
PHARDWARE_PTE_X86PAE HbPde, HbPte;
//
// If the PDPT entry is empty, allocate a new page for it.
// Otherwise we simply map the page at this entry.
//
index = ((ULONG) VirtualAddress) >> PDPT_SHIFT_X86PAE;
if(HbPdpt[index].Valid) {
HbPde = HbMapPte (PTE_TRANSFER_PDE, (ULONG)(HbPdpt[index].PageFrameNumber));
} else {
HbPde = HbNextSharedPage(PTE_TRANSFER_PDE, 0);
RtlZeroMemory (HbPde, PAGE_SIZE);
HbPdpt[index].PageFrameNumber = HiberPageFrames[PTE_TRANSFER_PDE];
HbPdpt[index].Valid = 1;
}
//
// If the PDE entry is empty, allocate a new page for it.
// Otherwise we simply map the page at this entry.
//
index = (((ULONG) VirtualAddress) >> PDE_SHIFT_X86PAE) & PDE_INDEX_MASK_X86PAE;
if(HbPde[index].Valid) {
HbPte = HbMapPte (PTE_WAKE_PTE, (ULONG)(HbPde[index].PageFrameNumber));
} else {
HbPte = HbNextSharedPage(PTE_WAKE_PTE, 0);
RtlZeroMemory (HbPte, PAGE_SIZE);
HbPde[index].PageFrameNumber = HiberPageFrames[PTE_WAKE_PTE];
HbPde[index].Write = 1;
HbPde[index].Valid = 1;
}
//
// Locate the PTE of VirtualAddress and set it to PageFrameNumber
//
index = (((ULONG) VirtualAddress) >> PTE_SHIFT_X86PAE) & PDE_INDEX_MASK_X86PAE;
HbPte[index].PageFrameNumber = PageFrameNumber;
HbPte[index].Write = 1;
HbPte[index].Valid = 1;
}
VOID
HiberSetupForWakeDispatch (
VOID
)
{
if(BlAmd64UseLongMode) {
//
// if system was hibernated in long mode
//
HiberSetupForWakeDispatchAmd64();
} else {
if(BlUsePae) {
//
// if system was hibernated in pae mode
//
HiberSetupForWakeDispatchPAE();
} else {
//
// if system was hibernated in 32-bit x86 mode
//
HiberSetupForWakeDispatchX86();
}
}
}
VOID
HiberSetupForWakeDispatchX86 (
VOID
)
{
PHARDWARE_PTE HbPde;
PHARDWARE_PTE HbPte;
PHARDWARE_PTE WakePte;
PHARDWARE_PTE TransVa;
ULONG TransPde;
ULONG WakePde;
ULONG PteEntry;
//
// Allocate a transistion CR3. A page directory and table which
// contains the hibernation PTEs
//
HbPde = HbNextSharedPage(PTE_TRANSFER_PDE, 0);
HbPte = HbNextSharedPage(PTE_WAKE_PTE, 0); // TRANSFER_PTE, 0);
RtlZeroMemory (HbPde, PAGE_SIZE);
RtlZeroMemory (HbPte, PAGE_SIZE);
//
// Set PDE to point to PTE
//
TransPde = ((ULONG) HiberVa) >> PDE_SHIFT;
HbPde[TransPde].PageFrameNumber = HiberPageFrames[PTE_WAKE_PTE];
HbPde[TransPde].Write = 1;
HbPde[TransPde].Valid = 1;
//
// Fill in the hiber PTEs
//
PteEntry = (((ULONG) HiberVa) >> PTE_SHIFT) & PTE_INDEX_MASK;
TransVa = &HbPte[PteEntry];
RtlCopyMemory (TransVa, HiberPtes, HIBER_PTES * sizeof(HARDWARE_PTE));
//
// Make another copy at the Va of the wake image hiber ptes
//
WakePte = HbPte;
WakePde = ((ULONG) HiberIdentityVa) >> PDE_SHIFT;
if (WakePde != TransPde) {
WakePte = HbNextSharedPage(PTE_WAKE_PTE, 0);
HbPde[WakePde].PageFrameNumber = HiberPageFrames[PTE_WAKE_PTE];
HbPde[WakePde].Write = 1;
HbPde[WakePde].Valid = 1;
}
PteEntry = (((ULONG) HiberIdentityVa) >> PTE_SHIFT) & PTE_INDEX_MASK;
TransVa = &WakePte[PteEntry];
RtlCopyMemory (TransVa, HiberPtes, HIBER_PTES * sizeof(HARDWARE_PTE));
//
// Set TransVa to be relative to the va of the transfer Cr3
//
HiberTransVa = (PVOID) (((PUCHAR) TransVa) - HiberVa + (PUCHAR) HiberIdentityVa);
}
VOID
HiberSetupForWakeDispatchPAE (
VOID
)
/*++
Routine Description:
Set up memory mappings for wake dispatch routine. This mapping will
be used in a "transfer mode" in which the mapping of loader has
already been discarded and the mapping of OS is not restored yet.
For PAE systems, PAE is enabled before entering this "transfer mode".
Arguments:
None
Return:
None
--*/
{
PHARDWARE_PTE_X86PAE HbPdpt;
ULONG i, TransferCR3;
//
// Borrow the pte at PTE_SOURCE temporarily for Pdpt
//
HbPdpt = HbNextSharedPage(PTE_SOURCE, 0);
RtlZeroMemory (HbPdpt, PAGE_SIZE);
TransferCR3 = HiberPageFrames[PTE_SOURCE];
//
// Map in the pages where the code of BlpEnablePAE() resides.
// This has to be a 1-1 mapping, i.e. the value of virtual
// address is the same as the value of physical address
//
HbMapPagePAE( HbPdpt,
(PVOID)(&BlpEnablePAEStart),
(ULONG)(&BlpEnablePAEStart) >> PAGE_SHIFT);
HbMapPagePAE( HbPdpt,
(PVOID)(&BlpEnablePAEEnd),
(ULONG)(&BlpEnablePAEEnd) >> PAGE_SHIFT);
//
// Map in the pages that is reserved for hibernation use
//
for (i = 0; i < HIBER_PTES; i++) {
HbMapPagePAE( HbPdpt,
(PUCHAR)HiberVa + PAGE_SIZE * i,
(*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
}
for (i = 0; i < HIBER_PTES; i++) {
HbMapPagePAE( HbPdpt,
(PUCHAR)HiberIdentityVa + PAGE_SIZE * i,
(*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
}
//
// Update the value of HiberPageFrames[PTE_TRANSFER_PDE] to the
// TransferCR3. The wake dispatch function will read this entry
// for the CR3 value in transfer mode
//
HiberPageFrames[PTE_TRANSFER_PDE] = TransferCR3;
//
// HiberTransVa points to the PTEs reserved for hibernation code.
// HiberTransVa is similar to HiberPtes but it is relative to
// transfer Cr3
//
HiberTransVa = (PVOID)((PUCHAR) HiberIdentityVa +
(PTE_WAKE_PTE << PAGE_SHIFT) +
sizeof(HARDWARE_PTE_X86PAE) *
(((ULONG_PTR)HiberIdentityVa >> PTE_SHIFT_X86PAE) & PTE_INDEX_MASK_X86PAE));
}
#define AMD64_MAPPING_LEVELS 4
typedef struct _HB_AMD64_MAPPING_INFO {
ULONG PteToUse;
ULONG AddressMask;
ULONG AddressShift;
} CONST HB_AMD64_MAPPING_INFO, *PHB_AMD64_MAPPING_INFO;
HB_AMD64_MAPPING_INFO HbAmd64MappingInfo[AMD64_MAPPING_LEVELS] =
{
{ PTE_WAKE_PTE, 0x1ff, 12 },
{ PTE_TRANSFER_PDE, 0x1ff, 21 },
{ PTE_DEST, 0x1ff, 30 },
{ PTE_SOURCE, 0x1ff, 39 }
};
#define _HARDWARE_PTE_WORKING_SET_BITS 11
typedef struct _HARDWARE_PTE_AMD64 {
ULONG64 Valid : 1;
ULONG64 Write : 1; // UP version
ULONG64 Owner : 1;
ULONG64 WriteThrough : 1;
ULONG64 CacheDisable : 1;
ULONG64 Accessed : 1;
ULONG64 Dirty : 1;
ULONG64 LargePage : 1;
ULONG64 Global : 1;
ULONG64 CopyOnWrite : 1; // software field
ULONG64 Prototype : 1; // software field
ULONG64 reserved0 : 1; // software field
ULONG64 PageFrameNumber : 28;
ULONG64 reserved1 : 24 - (_HARDWARE_PTE_WORKING_SET_BITS+1);
ULONG64 SoftwareWsIndex : _HARDWARE_PTE_WORKING_SET_BITS;
ULONG64 NoExecute : 1;
} HARDWARE_PTE_AMD64, *PHARDWARE_PTE_AMD64;
VOID
HbMapPageAmd64(
PHARDWARE_PTE_AMD64 RootLevelPageTable,
ULONGLONG VirtualAddress,
ULONG PageFrameNumber
)
/*++
Routine Description:
This functions maps a virtural address into Amd64 page mapping structure.
Arguments:
RootLevelPageTable - Supplies the base address of Page-Map Level-4 Table.
VirtualAddress - Supplies the virtual address to map
PageFrameNumber - Supplies the physical page number to be mapped
Return:
None
--*/
{
LONG i;
ULONG index, PteToUse;
PHARDWARE_PTE_AMD64 PageTable, PageTableTmp;
PageTable = RootLevelPageTable;
//
// Build a 4-level mapping top-down
//
for(i = AMD64_MAPPING_LEVELS - 1; i >= 0; i--) {
index = (ULONG)((VirtualAddress >> HbAmd64MappingInfo[i].AddressShift) &
HbAmd64MappingInfo[i].AddressMask);
if (i > 0) {
PteToUse = HbAmd64MappingInfo[i-1].PteToUse;
if (PageTable[index].Valid) {
//
// If a page table entry is valid we simply map the page
// at it and go to the next mapping level.
//
PageTable = HbMapPte(PteToUse,
(ULONG)PageTable[index].PageFrameNumber);
} else {
//
// If a page entry is invalid, we allocate a new page
// from the free page list and reference the new page
// from this page entry.
//
PageTableTmp = HbNextSharedPage(PteToUse, 0);
PageTable[index].PageFrameNumber = HiberPageFrames[PteToUse];
PageTable[index].Valid = 1;
PageTable[index].Write = 1;
RtlZeroMemory (PageTableTmp, PAGE_SIZE);
PageTable = PageTableTmp;
}
} else {
//
// Now we come to the PTE level. Set this PTE entry to the
// target page frame number.
//
PageTable[index].PageFrameNumber = PageFrameNumber;
PageTable[index].Valid = 1;
PageTable[index].Write = 1;
}
}
}
VOID
HiberSetupForWakeDispatchAmd64(
VOID
)
/*++
Routine Description:
This function prepares memory mapping for the transition period
where neither loader's nor kernel's mapping is available. The
processor runs in long mode in transition period.
We'll create mapping for the following virtual addresses
- the code handles long mode switch
- loader's HiberVa
- wake image's HiberVa
Arguments:
None
Return:
None
--*/
{
PHARDWARE_PTE_AMD64 HbTopLevelPTE;
ULONG i;
ULONG TransferCR3;
//
// Borrow the pte at PTE_SOURCE for temporary use here
//
HbTopLevelPTE = HbNextSharedPage(PTE_SOURCE, 0);
RtlZeroMemory (HbTopLevelPTE, PAGE_SIZE);
TransferCR3 = HiberPageFrames[PTE_SOURCE];
//
// Map in the pages that is holding thee code of _BlAmd64SwitchToLongMode.
// This has to be a 1-1 mapping, i.e. the virtual address equals physical
// address
//
HbMapPageAmd64(
HbTopLevelPTE,
(ULONGLONG)(&BlAmd64SwitchToLongModeStart),
(ULONG)(&BlAmd64SwitchToLongModeStart) >> PAGE_SHIFT);
HbMapPageAmd64(
HbTopLevelPTE,
(ULONGLONG)(&BlAmd64SwitchToLongModeEnd),
(ULONG)(&BlAmd64SwitchToLongModeEnd) >> PAGE_SHIFT);
//
// Map in the loader's HiberVa
//
for (i = 0; i < HIBER_PTES; i++) {
HbMapPageAmd64(HbTopLevelPTE,
(ULONGLONG)((ULONG_PTR)HiberVa + PAGE_SIZE * i),
(*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
}
//
// Map in the wake image's HiberVa. Note that the hiber pte at
// PTE_WAKE_PTE will be set to the the right value as a result
// of this mapping.
//
for (i = 0; i < HIBER_PTES; i++) {
HbMapPageAmd64(HbTopLevelPTE,
HiberIdentityVaAmd64 + PAGE_SIZE * i,
(*((PULONG)(HiberPtes) + i) >> PAGE_SHIFT));
}
//
// Update the value of HiberPageFrames[PTE_TRANSFER_PDE] to the
// TransferCR3. The wake dispatch function will read this entry
// for the CR3 value in transition mode
//
HiberPageFrames[PTE_TRANSFER_PDE] = TransferCR3;
//
// HiberTransVaAmd64 points to the wake image's hiber ptes. It is
// is relative to transfer Cr3
//
HiberTransVaAmd64 = HiberIdentityVaAmd64 +
(PTE_WAKE_PTE << PAGE_SHIFT) +
sizeof(HARDWARE_PTE_AMD64) * ((HiberIdentityVaAmd64 >> HbAmd64MappingInfo[0].AddressShift) & HbAmd64MappingInfo[0].AddressMask);
}