/*++ 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; KIRQL OldIrql2; 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_PFN2 (OldIrql2); InsertHeadList (&Process->PhysicalVadList, &PhysicalView->ListEntry); ControlArea->NumberOfMappedViews += 1; ControlArea->NumberOfUserReferences += 1; ASSERT (ControlArea->NumberOfSectionReferences != 0); UNLOCK_PFN2 (OldIrql2); 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 ); }