1744 lines
45 KiB
C
1744 lines
45 KiB
C
/*++
|
|
|
|
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"
|
|
|
|
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
|
|
);
|
|
|
|
#pragma alloc_text(PAGE,VpQueryAgpInterface)
|
|
#pragma alloc_text(PAGE,AgpReservePhysical)
|
|
#pragma alloc_text(PAGE,AgpReleasePhysical)
|
|
#pragma alloc_text(PAGE,AgpCommitPhysical)
|
|
#pragma alloc_text(PAGE,AgpFreePhysical)
|
|
#pragma alloc_text(PAGE,AgpReserveVirtual)
|
|
#pragma alloc_text(PAGE,AgpReleaseVirtual)
|
|
#pragma alloc_text(PAGE,AgpCommitVirtual)
|
|
#pragma alloc_text(PAGE,AgpFreeVirtual)
|
|
#pragma alloc_text(PAGE,VideoPortGetAgpServices)
|
|
#pragma alloc_text(PAGE,AddRangeToReservedRegion)
|
|
#pragma alloc_text(PAGE,RemoveRangeFromReservedRegion)
|
|
#pragma alloc_text(PAGE,CreateBitField)
|
|
#pragma alloc_text(PAGE,ModifyRegion)
|
|
#pragma alloc_text(PAGE,FindFirstRun)
|
|
|
|
VOID
|
|
DumpBitField(
|
|
PREGION Region
|
|
)
|
|
{
|
|
ULONG i;
|
|
ULONG Index = 0;
|
|
ULONG Mask = 1;
|
|
|
|
for (i=0; i<Region->Length; 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;
|
|
}
|
|
|
|
BOOLEAN
|
|
VpQueryAgpInterface(
|
|
PFDO_EXTENSION FdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send a QueryInterface Irp to our parent (the PCI bus driver) to
|
|
retrieve the AGP_BUS_INTERFACE.
|
|
|
|
Returns:
|
|
|
|
NT_STATUS code
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT Event;
|
|
PIRP QueryIrp = NULL;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PIO_STACK_LOCATION NextStack;
|
|
NTSTATUS Status;
|
|
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
QueryIrp = IoBuildSynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS,
|
|
FdoExtension->AttachedDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&Event,
|
|
&IoStatusBlock);
|
|
|
|
if (QueryIrp == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
NextStack = IoGetNextIrpStackLocation(QueryIrp);
|
|
|
|
//
|
|
// Set the default error code.
|
|
//
|
|
|
|
QueryIrp->IoStatus.Status = IoStatusBlock.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
//
|
|
// Set up for a QueryInterface Irp.
|
|
//
|
|
|
|
NextStack->MajorFunction = IRP_MJ_PNP;
|
|
NextStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
|
|
|
|
NextStack->Parameters.QueryInterface.InterfaceType = &GUID_AGP_BUS_INTERFACE_STANDARD;
|
|
NextStack->Parameters.QueryInterface.Size = sizeof(AGP_BUS_INTERFACE_STANDARD);
|
|
NextStack->Parameters.QueryInterface.Version = 1;
|
|
NextStack->Parameters.QueryInterface.Interface = (PINTERFACE) &FdoExtension->AgpInterface;
|
|
NextStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
|
|
|
FdoExtension->AgpInterface.Size = sizeof(AGP_BUS_INTERFACE_STANDARD);
|
|
FdoExtension->AgpInterface.Version = 1;
|
|
|
|
Status = IoCallDriver(FdoExtension->AttachedDeviceObject, QueryIrp);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
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;
|
|
PMDL Mdl;
|
|
|
|
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;
|
|
PMDL Mdl;
|
|
|
|
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);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
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,
|
|
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,
|
|
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,
|
|
(KPROCESSOR_MODE)KernelMode,
|
|
PhysicalContext->Caching ? MmCached : MmNonCached,
|
|
NULL,
|
|
TRUE,
|
|
HighPagePriority);
|
|
|
|
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);
|
|
SIZE_T Length;
|
|
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(ZwFreeVirtualMemory(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(ZwFreeVirtualMemory(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 = ZwAllocateVirtualMemory(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, ExcessPages;
|
|
|
|
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(ZwFreeVirtualMemory(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 ZwAllocateVirtualMemory 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(ZwAllocateVirtualMemory(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(ZwAllocateVirtualMemory(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;
|
|
HANDLE PhysicalMemoryHandle;
|
|
NTSTATUS ntStatus;
|
|
BOOLEAN bUserMode;
|
|
|
|
VirtualContext = (PVIRTUAL_RESERVE_CONTEXT) VirtualReserveContext;
|
|
PhysicalContext = VirtualContext->PhysicalReserveContext;
|
|
|
|
PhysicalAddress = PhysicalContext->PhysicalAddress;
|
|
PhysicalAddress.LowPart += Offset * PAGE_SIZE;
|
|
|
|
bUserMode = 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.
|
|
//
|
|
|
|
//
|
|
// Get a handle to the physical memory section using our pointer.
|
|
// If this fails, return.
|
|
//
|
|
|
|
ntStatus =
|
|
ObOpenObjectByPointer(
|
|
PhysicalMemorySection,
|
|
0L,
|
|
(PACCESS_STATE) NULL,
|
|
SECTION_ALL_ACCESS,
|
|
(POBJECT_TYPE) NULL,
|
|
KernelMode,
|
|
&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 =
|
|
ZwMapViewOfSection(
|
|
PhysicalMemoryHandle,
|
|
VirtualContext->ProcessHandle,
|
|
&VirtualAddress,
|
|
0,
|
|
Pages * PAGE_SIZE,
|
|
&PhysicalAddress,
|
|
&Length,
|
|
ViewUnmap,
|
|
0,
|
|
Protect);
|
|
|
|
pVideoDebugPrint((1, "ntStatus = 0x%x\n", ntStatus));
|
|
|
|
ZwClose(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,
|
|
(KPROCESSOR_MODE)UserMode,
|
|
PhysicalContext->Caching ? MmCached : MmNonCached,
|
|
(PVOID)((ULONG_PTR)VirtualContext->VirtualAddress + Offset * PAGE_SIZE),
|
|
TRUE,
|
|
HighPagePriority);
|
|
|
|
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) {
|
|
|
|
ZwUnmapViewOfSection(
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a set of AGP services to the caller.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - Pointer to the miniports device extension
|
|
|
|
AgpServices - A buffer in which to place the AGP services.
|
|
|
|
Returns:
|
|
|
|
TRUE if successful,
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension);
|
|
SYSTEM_BASIC_INFORMATION basicInfo ;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// This entry point is only valid for PnP Drivers.
|
|
//
|
|
|
|
if ((fdoExtension->Flags & LEGACY_DRIVER) == 0) {
|
|
|
|
if (VpQueryAgpInterface(fdoExtension)) {
|
|
|
|
//
|
|
// Fill in the list of function pointers.
|
|
//
|
|
|
|
AgpServices->AgpReservePhysical = AgpReservePhysical;
|
|
AgpServices->AgpCommitPhysical = AgpCommitPhysical;
|
|
AgpServices->AgpFreePhysical = AgpFreePhysical;
|
|
AgpServices->AgpReleasePhysical = AgpReleasePhysical;
|
|
|
|
AgpServices->AgpReserveVirtual = AgpReserveVirtual;
|
|
AgpServices->AgpCommitVirtual = AgpCommitVirtual;
|
|
AgpServices->AgpFreeVirtual = AgpFreeVirtual;
|
|
AgpServices->AgpReleaseVirtual = AgpReleaseVirtual;
|
|
|
|
status = ZwQuerySystemInformation (SystemBasicInformation,
|
|
&basicInfo,
|
|
sizeof(basicInfo),
|
|
NULL) ;
|
|
if (!NT_SUCCESS(status)) {
|
|
pVideoDebugPrint((0, "VIDEOPRT: Failed AGP system information.\n"));
|
|
return FALSE ;
|
|
}
|
|
|
|
AgpServices->AllocationLimit = (basicInfo.NumberOfPhysicalPages *
|
|
basicInfo.PageSize) / 8 ;
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
pVideoDebugPrint((1, "VideoPortGetAgpServices - only valid on PnP drivers\n"));
|
|
return FALSE;
|
|
}
|
|
}
|