windows-nt/Source/XPSP1/NT/base/ntos/mm/axp64/physsect.c

635 lines
17 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Copyright (c) 1992 Digital Equipment Corporation
Module Name:
physsect.c
Abstract:
This module contains the routine for mapping physical sections for
ALPHA machines.
Author:
Lou Perazzoli (loup) 22-May-1989
Joe Notarangelo 21-Sep-1992
Revision History:
--*/
#include "mi.h"
//#define FIRSTDBG 1
//#define AGGREGATE_DBG FIRSTDBG
static
ULONG
MaximumAlignment(
ULONG Offset
);
static
ULONG
AggregatePages(
PMMPTE,
PFN_NUMBER,
ULONG,
PULONG
);
NTSTATUS
MiMapViewOfPhysicalSection (
IN PCONTROL_AREA ControlArea,
IN PEPROCESS Process,
IN PVOID *CapturedBase,
IN PLARGE_INTEGER SectionOffset,
IN PSIZE_T CapturedViewSize,
IN ULONG ProtectionMask,
IN ULONG_PTR ZeroBits,
IN ULONG AllocationType,
IN BOOLEAN WriteCombined,
OUT PBOOLEAN ReleasedWsMutex
)
/*++
Routine Description:
This routine maps the specified physical section into the
specified process's address space.
Arguments:
see MmMapViewOfSection above...
ControlArea - Supplies the control area for the section.
Process - Supplies the process pointer which is receiving the section.
ProtectionMask - Supplies the initial page protection-mask.
ReleasedWsMutex - Supplies FALSE, receives TRUE if the working set
mutex is released.
Return Value:
Status of the map view operation.
Environment:
Kernel Mode, working set mutex and address creation mutex held.
--*/
{
PMMVAD Vad;
PVOID StartingAddress;
PVOID EndingAddress;
KIRQL OldIrql;
PMMPTE PointerPpe;
PMMPTE PointerPde;
PMMPTE PointerPte;
PMMPTE LastPte;
MMPTE TempPte;
PMMPFN Pfn2;
SIZE_T PhysicalViewSize;
ULONG Alignment;
ULONG PagesToMap;
PFN_NUMBER NextPfn;
PVOID UsedPageTableHandle;
PVOID UsedPageDirectoryHandle;
PMI_PHYSICAL_VIEW PhysicalView;
//
// Physical memory section.
//
#ifdef FIRSTDBG
DbgPrint( "MM: Physsect CaptureBase = %x SectionOffset = %x\n",
CapturedBase, SectionOffset->LowPart );
DbgPrint( "MM: Physsect Allocation Type = %x, MEM_LARGE_PAGES = %x\n",
AllocationType, MEM_LARGE_PAGES );
#endif //FIRSTDBG
//
// Compute the alignment we require for the virtual mapping.
// The default is 64K to match protection boundaries.
// Larger page sizes are used if MEM_LARGE_PAGES is requested.
// The Alpha AXP architecture supports granularity hints so that
// larger pages can be defined in the following multiples of
// PAGE_SIZE:
// 8**(GH) * PAGE_SIZE, where GH element of {0,1,2,3}
//
Alignment = X64K;
if( AllocationType & MEM_LARGE_PAGES ){
//
// MaxAlignment is the maximum boundary alignment of the
// SectionOffset (where the maximum boundary is one of the possible
// granularity hints boundaries)
//
ULONG MaxAlignment = MaximumAlignment( SectionOffset->LowPart );
Alignment = (MaxAlignment > Alignment) ? MaxAlignment : Alignment;
#ifdef FIRSTDBG
DbgPrint( "MM: Alignment = %x, SectionOffset = %x\n",
Alignment, SectionOffset->LowPart );
#endif //FIRSTDBG
}
LOCK_WS (Process);
if (*CapturedBase == NULL) {
//
// Attempt to locate address space. This could raise an
// exception.
//
try {
//
// Find a starting address on an alignment boundary.
//
PhysicalViewSize = (SectionOffset->LowPart + *CapturedViewSize) -
(ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart);
StartingAddress = MiFindEmptyAddressRange (PhysicalViewSize,
Alignment,
(ULONG)ZeroBits);
} except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress +
PhysicalViewSize - 1L) | (PAGE_SIZE - 1L));
StartingAddress = (PVOID)((ULONG_PTR)StartingAddress +
(SectionOffset->LowPart & (X64K - 1)));
if (ZeroBits > 0) {
if (EndingAddress > (PVOID)((LONG_PTR)0xFFFFFFFF >> ZeroBits)) {
return STATUS_NO_MEMORY;
}
}
} else {
//
// Check to make sure the specified base address to ending address
// is currently unused.
//
PhysicalViewSize = (SectionOffset->LowPart + *CapturedViewSize) -
(ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart);
StartingAddress = (PVOID)((ULONG_PTR)MI_64K_ALIGN(*CapturedBase) +
(SectionOffset->LowPart & (X64K - 1)));
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress +
*CapturedViewSize - 1L) | (PAGE_SIZE - 1L));
Vad = MiCheckForConflictingVad (StartingAddress, EndingAddress);
if (Vad != (PMMVAD)NULL) {
#if 0
MiDumpConflictingVad (StartingAddress, EndingAddress, Vad);
#endif
return STATUS_CONFLICTING_ADDRESSES;
}
}
//
// An unoccuppied address range has been found, build the virtual
// address descriptor to describe this range.
//
//
// Establish an exception handler and attempt to allocate
// the pool and charge quota. Note that the InsertVad routine
// will also charge quota which could raise an exception.
//
try {
PhysicalView = (PMI_PHYSICAL_VIEW)ExAllocatePoolWithTag (NonPagedPool,
sizeof(MI_PHYSICAL_VIEW),
MI_PHYSICAL_VIEW_KEY);
if (PhysicalView == NULL) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
Vad = (PMMVAD)ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD), ' daV');
if (Vad == NULL) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
PhysicalView->Vad = Vad;
PhysicalView->StartVa = StartingAddress;
PhysicalView->EndVa = EndingAddress;
Vad->StartingVpn = MI_VA_TO_VPN (StartingAddress);
Vad->EndingVpn = MI_VA_TO_VPN (EndingAddress);
Vad->ControlArea = ControlArea;
Vad->u.LongFlags = 0;
Vad->u2.VadFlags2.Inherit = ViewUnmap;
Vad->u.VadFlags.PhysicalMapping = 1;
Vad->u4.Banked = NULL;
// Vad->u.VadFlags.ImageMap = 0;
Vad->u.VadFlags.Protection = ProtectionMask;
Vad->u2.VadFlags2.CopyOnWrite = 0;
// Vad->u.VadFlags.LargePages = 0;
Vad->FirstPrototypePte =
(PMMPTE)(MI_CONVERT_PHYSICAL_BUS_TO_PFN(*SectionOffset));
//
// Set the first prototype PTE field in the Vad.
//
Vad->LastContiguousPte =
(PMMPTE)(MI_CONVERT_PHYSICAL_BUS_TO_PFN(*SectionOffset));
//
// Insert the VAD. This could get an exception.
//
MiInsertVad (Vad);
} except (EXCEPTION_EXECUTE_HANDLER) {
if (PhysicalView != NULL) {
ExFreePool (PhysicalView);
}
if (Vad != (PMMVAD)NULL) {
//
// The pool allocation suceeded, but the quota charge
// in InsertVad failed, deallocate the pool and return
// and error.
//
ExFreePool (Vad);
return GetExceptionCode();
}
return STATUS_INSUFFICIENT_RESOURCES;
}
// Increment the count of the number of views for the
// section object. This requires the PFN mutex to be held.
//
LOCK_AWE (Process, OldIrql);
LOCK_PFN_AT_DPC ();
if (PhysicalView->Vad->u.VadFlags.PhysicalMapping == 1) {
Process->HasPhysicalVad = 1;
}
InsertHeadList (&Process->PhysicalVadList, &PhysicalView->ListEntry);
ControlArea->NumberOfMappedViews += 1;
ControlArea->NumberOfUserReferences += 1;
ASSERT (ControlArea->NumberOfSectionReferences != 0);
UNLOCK_PFN_FROM_DPC ();
UNLOCK_AWE (Process, OldIrql);
//
// Build the PTEs in the address space.
//
PointerPpe = MiGetPpeAddress (StartingAddress);
PointerPde = MiGetPdeAddress (StartingAddress);
PointerPte = MiGetPteAddress (StartingAddress);
LastPte = MiGetPteAddress (EndingAddress);
#if defined (_WIN64)
MiMakePpeExistAndMakeValid (PointerPpe, Process, FALSE);
if (PointerPde->u.Long == 0) {
UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
ASSERT (MI_GET_USED_PTES_FROM_HANDLE (UsedPageDirectoryHandle) == 0);
MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
}
#endif
MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE);
Pfn2 = MI_PFN_ELEMENT(PointerPde->u.Hard.PageFrameNumber);
PagesToMap = (ULONG)((((ULONG_PTR)EndingAddress - (ULONG_PTR)StartingAddress))
+ (PAGE_SIZE-1) ) >> PAGE_SHIFT;
NextPfn = MI_CONVERT_PHYSICAL_BUS_TO_PFN(*SectionOffset);
#ifdef FIRSTDBG
DbgPrint( "MM: Physsect, PagesToMap = %x NextPfn = %x\n",
PagesToMap, NextPfn );
#endif //FIRSTDBG
MI_MAKE_VALID_PTE (TempPte,
NextPfn,
ProtectionMask,
PointerPte);
if (WriteCombined == TRUE) {
MI_SET_PTE_WRITE_COMBINE (TempPte);
}
if (TempPte.u.Hard.Write) {
TempPte.u.Hard.FaultOnWrite = 1;
}
while (PointerPte <= LastPte) {
ULONG PagesTogether;
ULONG GranularityHint;
//
// Compute the number of pages that can be mapped together
//
if( AllocationType & MEM_LARGE_PAGES ){
PagesTogether = AggregatePages( PointerPte,
NextPfn,
PagesToMap,
&GranularityHint );
} else {
PagesTogether = 1;
GranularityHint = 0;
}
#ifdef FIRSTDBG
DbgPrint( "MM: Physsect PointerPte = %x, NextPfn = %x\n",
PointerPte, NextPfn );
DbgPrint( "MM: Va = %x TempPte.Pfn = %x\n",
MiGetVirtualAddressMappedByPte( PointerPte ),
TempPte.u.Hard.PageFrameNumber );
DbgPrint( "MM: PagesToMap = %x\n", PagesToMap );
DbgPrint( "MM: PagesTogether = %x, GH = %x\n",
PagesTogether, GranularityHint );
#endif //FIRSTDBG
TempPte.u.Hard.GranularityHint = GranularityHint;
NextPfn += PagesTogether;
PagesToMap -= PagesTogether;
UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (MiGetVirtualAddressMappedByPte (PointerPte));
while( PagesTogether-- ){
if (MiIsPteOnPdeBoundary (PointerPte)) {
PointerPde = MiGetPteAddress (PointerPte);
if (MiIsPteOnPpeBoundary (PointerPte)) {
PointerPpe = MiGetPteAddress (PointerPde);
MiMakePpeExistAndMakeValid (PointerPpe, Process, FALSE);
if (PointerPde->u.Long == 0) {
UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
ASSERT (MI_GET_USED_PTES_FROM_HANDLE (UsedPageDirectoryHandle) == 0);
MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
}
}
MiMakePdeExistAndMakeValid (PointerPde, Process, FALSE);
Pfn2 = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (MiGetVirtualAddressMappedByPte (PointerPte));
}
ASSERT( PointerPte->u.Long == 0 );
*PointerPte = TempPte;
#if PFN_CONSISTENCY
LOCK_PFN (OldIrql);
#endif
Pfn2->u2.ShareCount += 1;
#if PFN_CONSISTENCY
UNLOCK_PFN (OldIrql);
#endif
//
// Increment the count of non-zero page table entries for this
// page table and the number of private pages for the process.
//
MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle);
PointerPte += 1;
TempPte.u.Hard.PageFrameNumber += 1;
} // while (PagesTogether-- )
} // while (PointerPte <= LastPte)
UNLOCK_WS (Process);
*ReleasedWsMutex = TRUE;
//
// Update the current virtual size in the process header.
//
*CapturedViewSize = (ULONG)((ULONG_PTR)EndingAddress - (ULONG_PTR)StartingAddress + 1L);
Process->VirtualSize += *CapturedViewSize;
if (Process->VirtualSize > Process->PeakVirtualSize) {
Process->PeakVirtualSize = Process->VirtualSize;
}
//
// Translate the virtual address to a quasi-virtual address for
// use by drivers that touch mapped devices. Note: the routine
// HalCreateQva will not translate the StartingAddress if the
// StartingAddress is within system memory address space.
//
// N.B. - It will not work to attempt map addresses that begin in
// system memory and extend through i/o space.
//
*CapturedBase = HalCreateQva( *SectionOffset, StartingAddress );
return STATUS_SUCCESS;
}
ULONG
MaximumAlignment(
IN ULONG Offset
)
/*++
Routine Description:
This routine returns the maximum granularity hint alignment boundary
to which Offset is naturally aligned.
Arguments:
Offset - Supplies the address offset to check for alignment.
Return Value:
The number which represents the largest natural alignment of Offset.
Environment:
--*/
{
if( (Offset & (GH3_PAGE_SIZE - 1)) == 0 ){
return GH3_PAGE_SIZE;
}
if( (Offset & (GH2_PAGE_SIZE - 1)) == 0 ){
return GH2_PAGE_SIZE;
}
if( (Offset & (GH1_PAGE_SIZE - 1)) == 0 ){
return GH1_PAGE_SIZE;
}
if( (Offset & (PAGE_SIZE - 1)) == 0 ){
return PAGE_SIZE;
}
return 0;
}
ULONG
AggregatePages(
IN PMMPTE PointerPte,
IN PFN_NUMBER Pfn,
IN ULONG Pages,
OUT PULONG GranularityHint
)
/*++
Routine Description:
This routine computes the number of standard size pages that can be
aggregated into a single large page and returns the granularity hint
for that size large page.
Arguments:
PointerPte - Supplies the PTE pointer for the starting virtual address
of the mapping.
Pfn - Supplies the starting page frame number of the memory to be
mapped.
Pages - Supplies the number of pages to map.
GranularityHint - Receives the granularity hint for the large page used
to aggregate the standard pages.
Return Value:
The number of pages that can be aggregated together.
Environment:
--*/
{
ULONG MaxVirtualAlignment;
ULONG MaxPhysicalAlignment;
ULONG MaxPageAlignment;
ULONG MaxAlignment;
//
// Determine the largest page that will map a maximum of Pages.
// The largest page must be both virtually and physically aligned
// to the large page size boundary.
// Determine the largest common alignment for the virtual and
// physical addresses, factor in Pages, and then match to the
// largest page size possible via the granularity hints.
//
MaxVirtualAlignment =
MaximumAlignment((ULONG)((ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPte)));
MaxPhysicalAlignment = MaximumAlignment( (ULONG)(Pfn << PAGE_SHIFT) );
MaxPageAlignment = (ULONG)(Pages << PAGE_SHIFT);
#ifdef AGGREGATE_DBG
DbgPrint( "MM: Aggregate MaxVirtualAlign = %x\n", MaxVirtualAlignment );
DbgPrint( "MM: Aggregate MaxPhysicalAlign = %x\n", MaxPhysicalAlignment );
DbgPrint( "MM: Aggregate MaxPageAlign = %x\n", MaxPageAlignment );
#endif //AGGREGATE_DBG
//
// Maximum alignment is the minimum of the virtual and physical alignments.
//
MaxAlignment = (MaxVirtualAlignment > MaxPhysicalAlignment) ?
MaxPhysicalAlignment : MaxVirtualAlignment;
MaxAlignment = (MaxAlignment > MaxPageAlignment) ?
MaxPageAlignment : MaxAlignment;
//
// Convert MaxAlignment to granularity hint value
//
if( (MaxAlignment & (GH3_PAGE_SIZE - 1)) == 0 ){
*GranularityHint = GH3;
} else if( (MaxAlignment & (GH2_PAGE_SIZE - 1)) == 0 ){
*GranularityHint = GH2;
} else if( (MaxAlignment & (GH1_PAGE_SIZE - 1)) == 0 ){
*GranularityHint = GH1;
} else if( (MaxAlignment & (PAGE_SIZE - 1)) == 0 ){
*GranularityHint = GH0;
} else {
*GranularityHint = GH0;
#if DBG
DbgPrint( "MM: Aggregate Physical pages - not page aligned\n" );
#endif //DBG
} // end, if then elseif
//
// Return number of pages aggregated.
//
return( MaxAlignment >> PAGE_SHIFT );
}