/*++ Copyright (c) 1990-1999 Microsoft Corporation Module Name: agp.c Abstract: This is the agp portion of the video port driver. Author: Erick Smith (ericks) Oct. 1997 Environment: kernel mode only Revision History: --*/ #include "videoprt.h" #if 0 // NUGOOP extern ULONG VideoDebugLevel; #if DBG #define pVideoDebugPrint(arg) VideoPortDebugPrint arg #else #define pVideoDebugPrint(arg) #endif // // Device extension aliases. // typedef PVIDEO_DEVICE_EXTENSION PFDO_EXTENSION; #define GET_FDO_EXT VIDEO_GET_VIDEO_DEVICE_EXTENSION // // AGP Data Structures // #define POOL_TAG 0x72745076 // 'vPrt' typedef struct _AGP_ALLOCATION { PHYSICAL_ADDRESS PhysicalAddress; PVOID VirtualAddress; ULONG Pages; PMDL Mdl; struct _AGP_ALLOCATION *Next; } AGP_ALLOCATION, *PAGP_ALLOCATION; typedef struct _REGION { ULONG Length; ULONG NumDwords; ULONG BitField[1]; } REGION, *PREGION; typedef struct _RESERVATION_LIST RESERVATION_LIST, *PRESERVATION_LIST; typedef struct _RESERVATION_LIST { ULONG Offset; ULONG Pages; ULONG RefCount; BOOLEAN Reserved; PRESERVATION_LIST Next; }; typedef struct _PHYSICAL_RESERVE_CONTEXT { ULONG Pages; ULONG Caching; PVOID MapHandle; PHYSICAL_ADDRESS PhysicalAddress; PREGION Region; } PHYSICAL_RESERVE_CONTEXT, *PPHYSICAL_RESERVE_CONTEXT; typedef struct _VIRTUAL_RESERVE_CONTEXT { HANDLE ProcessHandle; PEPROCESS Process; PVOID VirtualAddress; PPHYSICAL_RESERVE_CONTEXT PhysicalReserveContext; PRESERVATION_LIST ReservationList; PREGION Region; } VIRTUAL_RESERVE_CONTEXT, *PVIRTUAL_RESERVE_CONTEXT; PVOID AddRangeToReservedRegion( IN PVIRTUAL_RESERVE_CONTEXT VirtualContext, IN ULONG Pages, IN ULONG Offset ); VOID RemoveRangeFromReservedRegion( IN PVIRTUAL_RESERVE_CONTEXT VirtualContext, IN ULONG Pages, IN ULONG Offset ); VOID AgpFreePhysical( IN PVOID Context, IN PVOID PhysicalReserveContext, IN ULONG Pages, IN ULONG Offset ); VOID AgpFreeVirtual( IN PVOID Context, IN PVOID VirtualReserveContext, IN ULONG Pages, IN ULONG Offset ); VOID DumpBitField( PREGION Region ) { ULONG i; ULONG Index = 0; ULONG Mask = 1; for (i=0; iLength; i++) { if (Mask & Region->BitField[Index]) { pVideoDebugPrint((1, "1")); } else { pVideoDebugPrint((1, "0")); } Mask <<= 1; if (Mask == 0) { Index++; Mask = 1; } } pVideoDebugPrint((1, "\n")); } BOOLEAN CreateBitField( ULONG Length, PREGION *Region ) /*++ Routine Description: This routine creates and initializes a bitfield. Arguments: Length - Number of items to track. Region - Location in which to store the pointer to the REGION handle. Returns: TRUE - the the bitfield was created successfully, FALSE - otherwise. --*/ { ULONG NumDwords = (Length + 31) / 32; BOOLEAN bRet = FALSE; PREGION Buffer; Buffer = (PREGION) ExAllocatePoolWithTag(PagedPool, sizeof(REGION) + (NumDwords - 1) * sizeof(ULONG), POOL_TAG); if (Buffer) { Buffer->Length = Length; Buffer->NumDwords = NumDwords; memset(&Buffer->BitField[0], 0, NumDwords * 4); bRet = TRUE; } *Region = Buffer; return bRet; } VOID ModifyRegion( PREGION Region, ULONG Offset, ULONG Length, BOOLEAN Set ) /*++ Routine Description: Sets 'Length' bits starting at position 'Offset' in the bitfield. Arguments: Region - Pointer to the region to modify. Offset - Offset into the bitfield at which to start. Length - Number of bits to set. Set - TRUE if you want to set the region, FALSE to clear it. --*/ { ULONG Index = Offset / 32; ULONG Count = ((Offset + Length - 1) / 32) - Index; ULONG lMask = ~((1 << (Offset & 31)) - 1); ULONG rMask = ((1 << ((Offset + Length - 1) & 31)) * 2) - 1; PULONG ptr = &Region->BitField[Index]; ASSERT(Length != 0); if (Count == 0) { // // Only one DWORD is modified, so combine left and right masks. // lMask &= rMask; } if (Set) { *ptr++ |= lMask; while (Count > 1) { *ptr++ |= 0xFFFFFFFF; Count--; } if (Count) { *ptr |= rMask; } } else { *ptr++ &= ~lMask; while (Count > 1) { *ptr++ &= 0; Count--; } if (Count) { *ptr++ &= ~rMask; } } #if DBG pVideoDebugPrint((1, "Current BitField for Region: 0x%x\n", Region)); DumpBitField(Region); #endif } BOOLEAN FindFirstRun( PREGION Region, PULONG Offset, PULONG Length ) /*++ Routine Description: This routine finds the first run of bits in a bitfield. Arguments: Region - Pointer to the region to operate on. Offset - Pointer to a ULONG to hold the offset of the run. Length - Pointer to a ULONG to hold the length of a run. Returns: TRUE if a run was detected, FALSE otherwise. --*/ { PULONG ptr = Region->BitField; ULONG Index = 0; ULONG BitMask; ULONG lsb; ULONG Count; ULONG ptrVal; while ((Index < Region->NumDwords) && (*ptr == 0)) { ptr++; Index++; } if (Index == Region->NumDwords) { return FALSE; } // // Find least significant bit // lsb = 0; ptrVal = *ptr; BitMask = 1; while ((ptrVal & BitMask) == 0) { BitMask <<= 1; lsb++; } *Offset = (Index * 32) + lsb; // // Determine the run length // Count = 0; while (Index < Region->NumDwords) { if (ptrVal & BitMask) { BitMask <<= 1; Count++; if (BitMask == 0) { BitMask = 0x1; Index++; ptrVal = *++ptr; while ((ptrVal == 0xFFFFFFFF) && (Index < Region->NumDwords)) { Index++; Count += 32; ptrVal = *ptr++; } } } else { break; } } *Length = Count; return TRUE; } PHYSICAL_ADDRESS AgpReservePhysical( IN PVOID Context, IN ULONG Pages, IN ULONG Caching, OUT PVOID *PhysicalReserveContext ) /*++ Routine Description: Reserves a range of physical addresses for AGP. Arguments: Context - The Agp Context Pages - Number of pages to reserve Caching - Specifies the type of caching to use PhysicalReserveContext - Location to store our reservation context. Returns: The base of the physical address range reserved. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); PHYSICAL_ADDRESS PhysicalAddress = {0,0}; NTSTATUS status; PPHYSICAL_RESERVE_CONTEXT ReserveContext; PVOID MapHandle; pVideoDebugPrint((1, "AGP: Reserving 0x%x Pages of Address Space\n", Pages)); ReserveContext = ExAllocatePoolWithTag(PagedPool, sizeof(PHYSICAL_RESERVE_CONTEXT), POOL_TAG); if (ReserveContext) { if (CreateBitField(Pages, &ReserveContext->Region)) { status = fdoExtension->AgpInterface.ReserveMemory( fdoExtension->AgpInterface.AgpContext, Pages, Caching ? MmWriteCombined : MmNonCached, &MapHandle, &PhysicalAddress); if (NT_SUCCESS(status)) { ReserveContext->Pages = Pages; ReserveContext->Caching = Caching; ReserveContext->MapHandle = MapHandle; ReserveContext->PhysicalAddress = PhysicalAddress; } else { ExFreePool(ReserveContext->Region); goto FailureCase; } } else { goto FailureCase; } } *PhysicalReserveContext = ReserveContext; return PhysicalAddress; FailureCase: PhysicalAddress.QuadPart = 0; ExFreePool(ReserveContext); *PhysicalReserveContext = NULL; return PhysicalAddress; } VOID AgpReleasePhysical( PVOID Context, PVOID PhysicalReserveContext ) /*++ Routine Description: Reserves a range of reserved physical address. Arguments: Context - The Agp Context PhysicalReserveContext - The reservation context. Returns: none. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); PPHYSICAL_RESERVE_CONTEXT ReserveContext; ULONG Pages; ULONG Offset; ReserveContext = (PPHYSICAL_RESERVE_CONTEXT) PhysicalReserveContext; pVideoDebugPrint((1, "AGP: Releasing 0x%x Pages of Address Space\n", ReserveContext->Pages)); // // Make sure all pages have been freed // while (FindFirstRun(ReserveContext->Region, &Offset, &Pages)) { AgpFreePhysical(Context, PhysicalReserveContext, Pages, Offset); } fdoExtension->AgpInterface.ReleaseMemory(fdoExtension->AgpInterface.AgpContext, ReserveContext->MapHandle); ExFreePool(ReserveContext->Region); ExFreePool(ReserveContext); } BOOLEAN AgpCommitPhysical( PVOID Context, PVOID PhysicalReserveContext, ULONG Pages, ULONG Offset ) /*++ Routine Description: Reserves a range of physical addresses for AGP. Arguments: Context - The Agp Context PhysicalReserveContext - The reservation context. Pages - Number of pages to commit. Offset - The offset into the reserved region at which to commit the pages. Returns: TRUE if successful, FALSE otherwise. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); PHYSICAL_ADDRESS MemoryBase = {0,0}; PPHYSICAL_RESERVE_CONTEXT ReserveContext; NTSTATUS status; ReserveContext = (PPHYSICAL_RESERVE_CONTEXT) PhysicalReserveContext; status = fdoExtension->AgpInterface.CommitMemory(fdoExtension->AgpInterface.AgpContext, ReserveContext->MapHandle, Pages, Offset, NULL, &MemoryBase); if (NT_SUCCESS(status)) { ModifyRegion(ReserveContext->Region, Offset, Pages, TRUE); return TRUE; } else { return FALSE; } } VOID AgpFreePhysical( IN PVOID Context, IN PVOID PhysicalReserveContext, IN ULONG Pages, IN ULONG Offset ) /*++ Routine Description: Reserves a range of physical addresses for AGP. Arguments: Context - The Agp Context PhysicalReserveContext - The reservation context. Pages - Number of pages to release. Offset - The offset into the reserved region at which to release the pages. Returns: none. Notes: At the moment you must free the exact same offset/size as you commited. (ie. You can commit 10 pages at offset 2 and then free 5 pages at offset 4). --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); PPHYSICAL_RESERVE_CONTEXT ReserveContext; ReserveContext = (PPHYSICAL_RESERVE_CONTEXT) PhysicalReserveContext; fdoExtension->AgpInterface.FreeMemory(fdoExtension->AgpInterface.AgpContext, ReserveContext->MapHandle, Pages, Offset); ModifyRegion(ReserveContext->Region, Offset, Pages, FALSE); } PVOID AgpReserveVirtual( IN PVOID Context, IN HANDLE ProcessHandle, IN PVOID PhysicalReserveContext, OUT PVOID *VirtualReserveContext ) /*++ Routine Description: Reserves a range of virtual addresses for AGP. Arguments: Context - The Agp Context ProcessHandle - The handle of the process in which to reserve the virtual address range. PhysicalReserveContext - The physical reservation context to assoctiate with the given virtual reservation. VirtualReserveContext - The location in which to store the virtual reserve context. Returns: The base of the virtual address range reserved. Notes: You can't reserve a range of kernel address space, but if you want to commit into kernel space you still need a reservation handle. Pass in NULL for the process handle in this case. For the moment, we'll commit the entire region when the do a reservation in kernel space. Then Commit and Free will be no-ops. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); ULONG Protect = PAGE_READWRITE; PVIRTUAL_RESERVE_CONTEXT ReserveContext; PPHYSICAL_RESERVE_CONTEXT PhysicalContext; SIZE_T Length; PVOID VirtualAddress = NULL; PEPROCESS Process = NULL; PhysicalContext = (PPHYSICAL_RESERVE_CONTEXT) PhysicalReserveContext; Length = PhysicalContext->Pages * PAGE_SIZE; ReserveContext = ExAllocatePoolWithTag(PagedPool, sizeof(VIRTUAL_RESERVE_CONTEXT), POOL_TAG); if (ReserveContext) { if (CreateBitField(PhysicalContext->Pages, &ReserveContext->Region)) { // // BUGBUG: Fix this routine for kernel mode reservation! // if (!PhysicalContext->Caching) { Protect |= PAGE_NOCACHE; } // // Make sure we have the real process handle. // if (ProcessHandle == NtCurrentProcess()) { Process = PsGetCurrentProcess(); } ReserveContext->ProcessHandle = ProcessHandle; ReserveContext->Process = Process; ReserveContext->PhysicalReserveContext = (PPHYSICAL_RESERVE_CONTEXT) PhysicalReserveContext; ReserveContext->ReservationList = NULL; if (ProcessHandle) { VirtualAddress = AddRangeToReservedRegion(ReserveContext, PhysicalContext->Pages, 0); ASSERT(VirtualAddress != NULL); } else { // // BUGBUG: Find a better way to reserve in kernel space. // // For a kernel reservation, go ahead and commit the // entire range. // if (fdoExtension->AgpInterface.Capabilities & AGP_CAPABILITIES_MAP_PHYSICAL) { // // CPU can access AGP memory through AGP aperature. // VirtualAddress = MmMapIoSpace(PhysicalContext->PhysicalAddress.LowPart, PhysicalContext->Pages * PAGE_SIZE, PhysicalContext->Caching ? MmFrameBufferCached : 0); // // Not all systems support USWC, so if we attempted to map USWC // and failed, try again with just non-cached. // if ((VirtualAddress == NULL) && (PhysicalContext->Caching)) { VirtualAddress = MmMapIoSpace(PhysicalContext->PhysicalAddress.LowPart, PhysicalContext->Pages * PAGE_SIZE, MmNonCached); } } else { PMDL Mdl; // // Get the MDL for the range we are trying to map. // Mdl = MmCreateMdl(NULL, NULL, PhysicalContext->Pages * PAGE_SIZE); if (Mdl) { fdoExtension->AgpInterface.GetMappedPages( fdoExtension->AgpInterface.AgpContext, PhysicalContext->MapHandle, PhysicalContext->Pages, 0, Mdl); // // We must use the CPU's virtual memory mechanism to // make the non-contiguous MDL look contiguous. // VirtualAddress = MmMapLockedPagesSpecifyCache( Mdl, PhysicalContext->Caching ? MmCached : MmNonCached); ExFreePool(Mdl); } } } ReserveContext->VirtualAddress = VirtualAddress; } else { pVideoDebugPrint((1, "Couldn't create bit field.\n")); ExFreePool(ReserveContext); ReserveContext = NULL; } } else { pVideoDebugPrint((1, "AgpReserveVirtual: Out of memory.\n")); } *VirtualReserveContext = ReserveContext; return VirtualAddress; } VOID AgpReleaseVirtual( IN PVOID Context, IN PVOID VirtualReserveContext ) /*++ Routine Description: Releases a range of reserved virtual addresses. Arguments: Context - The Agp Context VirtualReserveContext - The reservation context. Returns: none. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); PVIRTUAL_RESERVE_CONTEXT VirtualContext; PPHYSICAL_RESERVE_CONTEXT PhysicalContext; ULONG Offset; ULONG Pages; VirtualContext = (PVIRTUAL_RESERVE_CONTEXT) VirtualReserveContext; PhysicalContext = VirtualContext->PhysicalReserveContext; if (VirtualContext->ProcessHandle) { // // Make sure all pages have been freed // while (FindFirstRun(VirtualContext->Region, &Offset, &Pages)) { AgpFreeVirtual(Context, VirtualReserveContext, Pages, Offset); } if (VirtualContext->VirtualAddress) { ASSERT(VirtualContext->ReservationList->Next == NULL); RemoveRangeFromReservedRegion(VirtualContext, VirtualContext->ReservationList->Pages, VirtualContext->ReservationList->Offset); if (VirtualContext->ReservationList) { ExFreePool(VirtualContext->ReservationList); VirtualContext->ReservationList = NULL; } } ExFreePool(VirtualContext->Region); ExFreePool(VirtualContext); } else { // // This was kernel virtual memory, so release the memory we // committed at reserve time. // if (fdoExtension->AgpInterface.Capabilities & AGP_CAPABILITIES_MAP_PHYSICAL) { MmUnmapIoSpace(VirtualContext->VirtualAddress, PhysicalContext->Pages * PAGE_SIZE); } else { PMDL Mdl; // // Get the MDL for the range we are trying to map. // Mdl = MmCreateMdl(NULL, NULL, PhysicalContext->Pages * PAGE_SIZE); if (Mdl) { fdoExtension->AgpInterface.GetMappedPages( fdoExtension->AgpInterface.AgpContext, PhysicalContext->MapHandle, PhysicalContext->Pages, 0, Mdl); MmUnmapLockedPages( VirtualContext->VirtualAddress, Mdl); ExFreePool(Mdl); } else { // // We couldn't free the memory because we couldn't allocate // memory for the MDL. We can free a small chunk at a time // by using a MDL on the stack. // ASSERT(FALSE); } } } } PVOID AddRangeToReservedRegion( IN PVIRTUAL_RESERVE_CONTEXT VirtualContext, IN ULONG Pages, IN ULONG Offset ) /*++ Routine Description: Adds a range to a reserved region of virtual address space. Arguments: VirtualContext - The context of the virtual reservation. Pages - Length of the region to release. Offset - Offset into region at which to start releasing pages. Returns: none. --*/ { PULONG VirtualAddress = NULL; SIZE_T Length; ULONG Protect = PAGE_READWRITE; NTSTATUS Status; PRESERVATION_LIST Curr, Prev = NULL; pVideoDebugPrint((1, "AddPagesToReservedRange\n")); pVideoDebugPrint((1, " Pages = 0x%x\n Offset = 0x%x\n", Pages, Offset)); // // Adjust Pages and Offset such that will always deal with 64K chunks. // Pages = Pages + (Offset & 0xf); // grow pages due to decrease offset Offset = Offset & ~0xf; // shrink offset to make it multiple of 16 Pages = (Pages + 0xf) & ~0xf; // grow pages to make it multiple of 16 pVideoDebugPrint((1, " Pages = 0x%x\n Offset = 0x%x\n", Pages, Offset)); if (VirtualContext->ReservationList) { // // We are making the assumption that if range is added, and we // already have a reservation list, that we had removed a region // because we mapped it, and now we are adding it back. So we // should be able to scan for a region, and just set the // reserved bit to TRUE. We can then concatinate with existing // right or left regions. // Curr = VirtualContext->ReservationList; while (Curr) { if (Curr->Offset == Offset) break; Prev = Curr; Curr = Curr->Next; } pVideoDebugPrint((1, "Region to Reserve:\n" " Offset: 0x%x\n" " Pages: 0x%x\n", Curr->Offset, Curr->Pages)); // // If the region is already reserved, simply return. // if (Curr->Reserved == TRUE) { pVideoDebugPrint((1, "Region is already reserved!\n")); return NULL; } // // If there is still an allocation in this region, then return. // if (--Curr->RefCount) { pVideoDebugPrint((1, "Region is still in use.\n")); return NULL; } pVideoDebugPrint((1, "Marking region as reserved.\n")); Curr->Reserved = TRUE; // // See if we can concatinate with the left or right region. // if (Prev && (Prev->Reserved)) { // // Release the region which we are concatinating with. // VirtualAddress = (PULONG)((ULONG_PTR)VirtualContext->VirtualAddress + Prev->Offset * PAGE_SIZE); Length = Prev->Pages * PAGE_SIZE; if (!VirtualContext->PhysicalReserveContext->Caching) { Protect |= PAGE_NOCACHE; } pVideoDebugPrint((1, "Freeing reserved region of length 0x%x at address 0x%p\n", (ULONG)Length, VirtualAddress)); if (!NT_SUCCESS(NtFreeVirtualMemory(VirtualContext->ProcessHandle, &VirtualAddress, &Length, MEM_RELEASE))) { ASSERT(FALSE); } pVideoDebugPrint((1, "Concatenating with previous range.\n")); Prev->Pages += Curr->Pages; Prev->Next = Curr->Next; ExFreePool(Curr); Curr = Prev; } if (Curr->Next && (Curr->Next->Reserved)) { PRESERVATION_LIST Next = Curr->Next; // // Release the region which we are concatinating with. // VirtualAddress = (PULONG)((ULONG_PTR)VirtualContext->VirtualAddress + Next->Offset * PAGE_SIZE); Length = Next->Pages * PAGE_SIZE; if (!VirtualContext->PhysicalReserveContext->Caching) { Protect |= PAGE_NOCACHE; } pVideoDebugPrint((1, "Freeing reserved region of length 0x%x at address 0x%p\n", (ULONG)Length, VirtualAddress)); if (!NT_SUCCESS(NtFreeVirtualMemory(VirtualContext->ProcessHandle, &VirtualAddress, &Length, MEM_RELEASE))) { ASSERT(FALSE); } pVideoDebugPrint((1, "Concatenating with next range.\n")); Curr->Pages += Next->Pages; Curr->Next = Next->Next; ExFreePool(Next); } pVideoDebugPrint((1, "Expanded Region:\n" " Offset: 0x%x\n" " Pages: 0x%x\n", Curr->Offset, Curr->Pages)); VirtualAddress = (PULONG)((ULONG_PTR)VirtualContext->VirtualAddress + Curr->Offset * PAGE_SIZE); } else { // // Indicate we don't care where we reserve from. // VirtualAddress = NULL; Curr = ExAllocatePoolWithTag(PagedPool, sizeof(RESERVATION_LIST), POOL_TAG); if (Curr) { Curr->Offset = Offset; Curr->Pages = Pages; Curr->RefCount = 0; Curr->Reserved = TRUE; Curr->Next = NULL; VirtualContext->ReservationList = Curr; } else { return NULL; } } Length = Curr->Pages * PAGE_SIZE; if (!VirtualContext->PhysicalReserveContext->Caching) { Protect |= PAGE_NOCACHE; } pVideoDebugPrint((1, "Reserving 0x%x bytes at virtual address 0x%p\n", (ULONG)Length, VirtualAddress)); Status = NtAllocateVirtualMemory(VirtualContext->ProcessHandle, &VirtualAddress, 0, &Length, MEM_RESERVE, Protect); if (!NT_SUCCESS(Status)) { pVideoDebugPrint((1, "Status = 0x%x\n", Status)); ASSERT(FALSE); return NULL; } return VirtualAddress; } VOID RemoveRangeFromReservedRegion( IN PVIRTUAL_RESERVE_CONTEXT VirtualContext, IN ULONG Pages, IN ULONG Offset ) /*++ Routine Description: Release a sub range of a reserved region of virtual address space. Arguments: VirtualContext - The context of the virtual reservation. Pages - Length of the region to release. Offset - Offset into region at which to start releasing pages. Returns: none. --*/ { PRESERVATION_LIST ReservationList, Curr, Prev = NULL; PULONG VirtualAddress = NULL; SIZE_T Length; ULONG Protect = PAGE_READWRITE; ULONG_PTR Address; pVideoDebugPrint((1, "RemovePagesFromReservedRange\n")); pVideoDebugPrint((1, " Pages = 0x%x\n Offset = 0x%x\n", Pages, Offset)); // // Adjust Pages and Offset such that will always deal with 64K chunks. // Pages = Pages + (Offset & 0xf); // grow pages due to decrease offset Offset = Offset & ~0xf; // shrink offset to make it multiple of 16 Pages = (Pages + 0xf) & ~0xf; // grow pages to make it multiple of 16 pVideoDebugPrint((1, " Pages = 0x%x\n Offset = 0x%x\n", Pages, Offset)); ASSERT(VirtualContext->ReservationList != NULL); Curr = VirtualContext->ReservationList; while (Curr) { if ((Curr->Offset <= Offset) && ((Curr->Offset + Curr->Pages) > Offset)) { break; } Prev = Curr; Curr = Curr->Next; } pVideoDebugPrint((1, "Range to fill into:\n" " Offset: 0x%x\n" " Pages: 0x%x\n", Curr->Offset, Curr->Pages)); ASSERT(Curr != NULL); // // If the region is already free, simply return. // if (Curr->Reserved == FALSE) { Curr->RefCount++; return; } // // Release the region which we are mapping into. // VirtualAddress = (PULONG)((ULONG_PTR)VirtualContext->VirtualAddress + Curr->Offset * PAGE_SIZE); Length = Curr->Pages * PAGE_SIZE; if (!VirtualContext->PhysicalReserveContext->Caching) { Protect |= PAGE_NOCACHE; } pVideoDebugPrint((1, "Freeing reserved region of length 0x%x at address 0x%p\n", (ULONG)Length, VirtualAddress)); if (!NT_SUCCESS(NtFreeVirtualMemory(VirtualContext->ProcessHandle, &VirtualAddress, &Length, MEM_RELEASE))) { ASSERT(FALSE); } // // Mark the region as not reserved. // Curr->Reserved = FALSE; Curr->RefCount++; // // Now see if we have sub regions to re-reserve. // if (Curr->Offset < Offset) { // // There will be a left over region prior to Curr. // ReservationList = ExAllocatePoolWithTag(PagedPool, sizeof(RESERVATION_LIST), POOL_TAG); if (ReservationList) { ReservationList->Offset = Curr->Offset; ReservationList->Pages = Offset - Curr->Offset; ReservationList->RefCount = 0; ReservationList->Next = Curr; ReservationList->Reserved = TRUE; Curr->Pages -= Offset - Curr->Offset; Curr->Offset = Offset; } else { return; } if (Prev) { Prev->Next = ReservationList; } else { VirtualContext->ReservationList = ReservationList; } pVideoDebugPrint((1, "Creating 'left' subregion\n" " Offset: 0x%x\n" " Pages: 0x%x\n", ReservationList->Offset, ReservationList->Pages)); // // Calculate the virtual address and length of the memory to reserve. // Note that NtAllocateVirtualMemory always rounds to a 64K boundary, // so we'll have to round up to prevent it from rounding down. // Address = ((ULONG_PTR)VirtualContext->VirtualAddress + ReservationList->Offset * PAGE_SIZE); VirtualAddress = (PULONG)Address; Length = ReservationList->Pages * PAGE_SIZE; pVideoDebugPrint((1, "Reserving memory range of length 0x%x at location 0x%p\n", (ULONG)Length, VirtualAddress)); if (!NT_SUCCESS(NtAllocateVirtualMemory(VirtualContext->ProcessHandle, &VirtualAddress, 0, &Length, MEM_RESERVE, Protect))) { ASSERT(FALSE); } } if (Curr->Pages > Pages) { // // There will be a left over region after Curr. // ReservationList = ExAllocatePoolWithTag(PagedPool, sizeof(RESERVATION_LIST), POOL_TAG); if (ReservationList) { ReservationList->Offset = Curr->Offset + Pages; ReservationList->Pages = Curr->Pages - Pages; ReservationList->RefCount = 0; ReservationList->Reserved = TRUE; Curr->Pages = Pages; } else { // // BUGBUG: // // If we return here then we have to adjacent blocks // which are both reserved. This will not cause a // problem, but it will never be cleaned up. // return; } pVideoDebugPrint((1, "Creating 'right' subregion\n" " Offset: 0x%x\n" " Pages: 0x%x\n", ReservationList->Offset, ReservationList->Pages)); ReservationList->Next = Curr->Next; Curr->Next = ReservationList; VirtualAddress = (PULONG)((ULONG_PTR)VirtualContext->VirtualAddress + ReservationList->Offset * PAGE_SIZE); Length = ReservationList->Pages * PAGE_SIZE; pVideoDebugPrint((1, "Reserving memory range of length 0x%x at location 0x%p\n", (ULONG)Length, VirtualAddress)); if (!NT_SUCCESS(NtAllocateVirtualMemory(VirtualContext->ProcessHandle, &VirtualAddress, 0, &Length, MEM_RESERVE, Protect))) { ASSERT(FALSE); } } return; } PVOID AgpCommitVirtual( IN PVOID Context, IN PVOID VirtualReserveContext, IN ULONG Pages, IN ULONG Offset ) /*++ Routine Description: Reserves a range of physical addresses for AGP. Arguments: Context - The Agp Context VirtualReserveContext - The reservation context. Pages - Number of pages to commit. Offset - The offset into the reserved region at which to commit the pages. Returns: The virtual address for the base of the commited pages. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); PVIRTUAL_RESERVE_CONTEXT VirtualContext; PPHYSICAL_RESERVE_CONTEXT PhysicalContext; PHYSICAL_ADDRESS PhysicalAddress; PVOID VirtualAddress = NULL; PVOID PhysicalMemorySection = NULL; HANDLE PhysicalMemoryHandle; NTSTATUS ntStatus; BOOLEAN bUserMode; VirtualContext = (PVIRTUAL_RESERVE_CONTEXT) VirtualReserveContext; PhysicalContext = VirtualContext->PhysicalReserveContext; PhysicalAddress = PhysicalContext->PhysicalAddress; PhysicalAddress.LowPart += Offset * PAGE_SIZE; bUserMode = (BOOLEAN)(VirtualContext->ProcessHandle != NULL); if (bUserMode) { // // First release the VA range we are going to map into. // RemoveRangeFromReservedRegion(VirtualContext, Pages, Offset); if (fdoExtension->AgpInterface.Capabilities & AGP_CAPABILITIES_MAP_PHYSICAL) { // // CPU can access AGP memory through AGP aperature. // ASSERT(FALSE); // // Get a handle to the physical memory section using our pointer. // If this fails, return. // ntStatus = ObOpenObjectByPointer( PhysicalMemorySection, (POBJECT_TYPE) NULL, &PhysicalMemoryHandle); // // If successful, map the memory. // if (NT_SUCCESS(ntStatus)) { SIZE_T Length = Pages * PAGE_SIZE; ULONG Protect = PAGE_READWRITE; VirtualAddress = ((PUCHAR)VirtualContext->VirtualAddress + Offset * PAGE_SIZE); if (!PhysicalContext->Caching) { Protect |= PAGE_NOCACHE; } ntStatus = NtMapViewOfSection( PhysicalMemoryHandle, VirtualContext->ProcessHandle, &VirtualAddress, 0, Pages * PAGE_SIZE, &PhysicalAddress, &Length, ViewUnmap, 0, Protect); pVideoDebugPrint((1, "ntStatus = 0x%x\n", ntStatus)); NtClose(PhysicalMemoryHandle); } } else { PMDL Mdl; // // Get the MDL for the range we are trying to map. // Mdl = MmCreateMdl(NULL, NULL, Pages * PAGE_SIZE); if (Mdl) { fdoExtension->AgpInterface.GetMappedPages( fdoExtension->AgpInterface.AgpContext, PhysicalContext->MapHandle, Pages, Offset, Mdl); // // We must use the CPU's virtual memory mechanism to // make the non-contiguous MDL look contiguous. // VirtualAddress = MmMapLockedPagesSpecifyCache( Mdl, PhysicalContext->Caching ? MmCached : MmNonCached); ExFreePool(Mdl); } } if (VirtualAddress) { ModifyRegion(VirtualContext->Region, Offset, Pages, TRUE); } } else { // // Kernel mode commit. Do nothing. // VirtualAddress = ((PUCHAR)VirtualContext->VirtualAddress + Offset * PAGE_SIZE); } return VirtualAddress; } VOID AgpFreeVirtual( IN PVOID Context, IN PVOID VirtualReserveContext, IN ULONG Pages, IN ULONG Offset ) /*++ Routine Description: Frees a range of virtual addresses. Arguments: Context - The Agp Context VirtualReserveContext - The reservation context. Pages - Number of pages to release. Offset - The offset into the reserved region at which to release the pages. Returns: none. Notes: At the moment you must free the exact same offset/size as you commited. (ie. You can commit 10 pages at offset 2 and then free 5 pages at offset 4). --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(Context); PVIRTUAL_RESERVE_CONTEXT VirtualContext; PPHYSICAL_RESERVE_CONTEXT PhysicalContext; PVOID VirtualAddress; VirtualContext = (PVIRTUAL_RESERVE_CONTEXT) VirtualReserveContext; PhysicalContext = VirtualContext->PhysicalReserveContext; VirtualAddress = (PUCHAR)((ULONG_PTR)VirtualContext->VirtualAddress + Offset * PAGE_SIZE); // // Make sure we are in the correct process context. // if (VirtualContext->ProcessHandle != NULL) { if (fdoExtension->AgpInterface.Capabilities & AGP_CAPABILITIES_MAP_PHYSICAL) { if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS) { NtUnmapViewOfSection( VirtualContext->ProcessHandle, VirtualAddress); } else { MmUnmapIoSpace(VirtualAddress, Pages * PAGE_SIZE); } } else { PMDL Mdl; // // Get the MDL for the range we are trying to map. // Mdl = MmCreateMdl(NULL, NULL, Pages * PAGE_SIZE); if (Mdl) { fdoExtension->AgpInterface.GetMappedPages( fdoExtension->AgpInterface.AgpContext, PhysicalContext->MapHandle, Pages, Offset, Mdl); MmUnmapLockedPages( VirtualAddress, Mdl); ExFreePool(Mdl); } else { // // We couldn't free the memory because we couldn't allocate // memory for the MDL. We can free a small chunk at a time // by using a MDL on the stack. // ASSERT(FALSE); } } if (VirtualAddress <= MM_HIGHEST_USER_ADDRESS) { AddRangeToReservedRegion(VirtualContext, Pages, Offset); } ModifyRegion(VirtualContext->Region, Offset, Pages, FALSE); } else { // // Kernel Space Free - do nothing. // } } BOOLEAN VideoPortGetAgpServices( IN PVOID HwDeviceExtension, OUT PVIDEO_PORT_AGP_SERVICES AgpServices ) { NTSTATUS status; SYSTEM_BASIC_INFORMATION BasicInformation; // // Figure out how much physical memory is in the system. // status = NtQuerySystemInformation(SystemBasicInformation, &BasicInformation, sizeof(SYSTEM_BASIC_INFORMATION), NULL); if (!NT_SUCCESS(status)) { return FALSE; } // // Fill in the AGP services table. // AgpServices->AgpReservePhysical = AgpReservePhysical; AgpServices->AgpCommitPhysical = AgpCommitPhysical; AgpServices->AgpFreePhysical = AgpFreePhysical; AgpServices->AgpReleasePhysical = AgpReleasePhysical; AgpServices->AgpReserveVirtual = AgpReserveVirtual; AgpServices->AgpCommitVirtual = AgpCommitVirtual; AgpServices->AgpFreeVirtual = AgpFreeVirtual; AgpServices->AgpReleaseVirtual = AgpReleaseVirtual; AgpServices->AllocationLimit = (BasicInformation.NumberOfPhysicalPages << PAGE_SHIFT) / 8; return TRUE; } #endif