windows-nt/Source/XPSP1/NT/base/busdrv/agp/agplib/intrface.c
2020-09-26 16:20:57 +08:00

1190 lines
29 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
intrface.c
Abstract:
Routines for implementing the AGP_BUS_INTERFACE_STANDARD interface
Author:
John Vert (jvert) 10/26/1997
Revision History:
Elliot Shmukler (elliots) 3/24/1999 - Added support for "favored" memory
ranges for AGP physical memory allocation,
fixed some bugs.
--*/
#define INITGUID 1
#include "agplib.h"
VOID
AgpLibFlushDcacheMdl(
PMDL Mdl
);
VOID
ApFlushDcache(
IN PKDPC Dpc,
IN PKEVENT Event,
IN PMDL Mdl,
IN PVOID SystemArgument2
);
PMDL
AgpCombineMdlList(IN PMDL MdlList);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, AgpInterfaceReference)
#pragma alloc_text(PAGE, AgpInterfaceDereference)
#pragma alloc_text(PAGE, AgpInterfaceReserveMemory)
#pragma alloc_text(PAGE, AgpInterfaceReleaseMemory)
#pragma alloc_text(PAGE, AgpInterfaceSetRate)
#pragma alloc_text(PAGE, AgpInterfaceCommitMemory)
#pragma alloc_text(PAGE, AgpInterfaceFreeMemory)
#pragma alloc_text(PAGE, AgpLibFlushDcacheMdl)
#pragma alloc_text(PAGE, AgpLibAllocatePhysicalMemory)
#pragma alloc_text(PAGE, AgpLibAllocateMappedPhysicalMemory)
#pragma alloc_text(PAGE, AgpCombineMdlList)
#endif
VOID
AgpInterfaceReference(
IN PMASTER_EXTENSION Extension
)
/*++
Routine Description:
References an interface. Currently a NOP.
Arguments:
Extension - Supplies the device extension
Return Value:
None
--*/
{
PAGED_CODE();
InterlockedIncrement(&Extension->InterfaceCount);
}
VOID
AgpInterfaceDereference(
IN PMASTER_EXTENSION Extension
)
/*++
Routine Description:
Dereferences an interface. Currently a NOP.
Arguments:
Extension - Supplies the device extension
Return Value:
None
--*/
{
PAGED_CODE();
InterlockedDecrement(&Extension->InterfaceCount);
}
NTSTATUS
AgpInterfaceReserveMemory(
IN PMASTER_EXTENSION Extension,
IN ULONG NumberOfPages,
IN MEMORY_CACHING_TYPE MemoryType,
OUT PVOID *MapHandle,
OUT OPTIONAL PHYSICAL_ADDRESS *PhysicalAddress
)
/*++
Routine Description:
Reserves memory in the specified aperture
Arguments:
Extension - Supplies the device extension where physical address space should be reserved.
NumberOfPages - Supplies the number of pages to reserve.
MemoryType - Supplies the memory caching type.
MapHandle - Returns the mapping handle to be used on subsequent calls.
PhysicalAddress - If present, returns the physical address in the aperture of the reserved
space
Return Value:
NTSTATUS
--*/
{
PVOID AgpContext;
NTSTATUS Status;
PHYSICAL_ADDRESS MemoryBase;
PAGP_RANGE Range;
PAGED_CODE();
AgpContext = GET_AGP_CONTEXT_FROM_MASTER(Extension);
Range = ExAllocatePoolWithTag(PagedPool,
sizeof(AGP_RANGE),
'RpgA');
if (Range == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
Range->CommittedPages = 0;
Range->NumberOfPages = NumberOfPages;
Range->Type = MemoryType;
LOCK_MASTER(Extension);
Status = AgpReserveMemory(AgpContext,
Range);
UNLOCK_MASTER(Extension);
if (!NT_SUCCESS(Status)) {
AGPLOG(AGP_CRITICAL,
("AgpInterfaceReserveMemory - reservation of %x pages of type %d failed %08lx\n",
NumberOfPages,
MemoryType,
Status));
} else {
AGPLOG(AGP_NOISE,
("AgpInterfaceReserveMemory - reserved %x pages of type %d at %I64X\n",
NumberOfPages,
MemoryType,
Range->MemoryBase.QuadPart));
}
*MapHandle = Range;
if (ARGUMENT_PRESENT(PhysicalAddress)) {
*PhysicalAddress = Range->MemoryBase;
}
return(Status);
}
NTSTATUS
AgpInterfaceReleaseMemory(
IN PMASTER_EXTENSION Extension,
IN PVOID MapHandle
)
/*++
Routine Description:
Releases memory in the specified aperture that was previously reserved by
AgpInterfaceReserveMemory
Arguments:
Extension - Supplies the device extension where physical address space should be reserved.
MapHandle - Supplies the mapping handle returned from AgpInterfaceReserveMemory
Return Value:
NTSTATUS
--*/
{
PAGP_RANGE Range;
PVOID AgpContext;
NTSTATUS Status;
PHYSICAL_ADDRESS MemoryBase;
PAGED_CODE();
AgpContext = GET_AGP_CONTEXT_FROM_MASTER(Extension);
Range = (PAGP_RANGE)MapHandle;
LOCK_MASTER(Extension);
//
// Make sure the range is empty
//
ASSERT(Range->CommittedPages == 0);
if (Range->CommittedPages != 0) {
AGPLOG(AGP_CRITICAL,
("AgpInterfaceReleaseMemory - Invalid attempt to release non-empty range %08lx\n",
Range));
UNLOCK_MASTER(Extension);
return(STATUS_INVALID_PARAMETER);
}
AGPLOG(AGP_NOISE,
("AgpInterfaceReleaseMemory - releasing range %08lx, %lx pages at %08lx\n",
Range,
Range->NumberOfPages,
Range->MemoryBase.QuadPart));
Status = AgpReleaseMemory(AgpContext,
Range);
if (!NT_SUCCESS(Status)) {
AGPLOG(AGP_CRITICAL,
("AgpInterfaceReleaseMemory - release failed %08lx\n",
Status));
}
UNLOCK_MASTER(Extension);
ExFreePool(Range);
return(Status);
}
NTSTATUS
AgpInterfaceCommitMemory(
IN PMASTER_EXTENSION Extension,
IN PVOID MapHandle,
IN ULONG NumberOfPages,
IN ULONG OffsetInPages,
IN OUT PMDL Mdl OPTIONAL,
OUT PHYSICAL_ADDRESS *MemoryBase
)
/*++
Routine Description:
Commits memory into the specified aperture that was previously reserved by
AgpInterfaceReserveMemory
Arguments:
Extension - Supplies the device extension where physical address space should
be committed.
MapHandle - Supplies the mapping handle returned from AgpInterfaceReserveMemory
NumberOfPages - Supplies the number of pages to be committed.
OffsetInPages - Supplies the offset, in pages, into the aperture reserved by
AgpInterfaceReserveMemory
Mdl - Returns the MDL describing the pages of memory committed.
MemoryBase - Returns the physical memory address of the committed memory.
Return Value:
NTSTATUS
--*/
{
PAGP_RANGE Range = (PAGP_RANGE)MapHandle;
PMDL NewMdl;
PVOID AgpContext;
NTSTATUS Status=STATUS_SUCCESS;
ULONG RunLength, RunOffset;
ULONG CurrentLength, CurrentOffset;
PMDL FirstMdl=NULL;
PAGED_CODE();
AgpContext = GET_AGP_CONTEXT_FROM_MASTER(Extension);
ASSERT(NumberOfPages <= Range->NumberOfPages);
ASSERT(NumberOfPages > 0);
ASSERT((Mdl == NULL) || (Mdl->ByteCount == PAGE_SIZE * NumberOfPages));
CurrentLength = NumberOfPages;
CurrentOffset = OffsetInPages;
LOCK_MASTER(Extension);
do {
//
// Save ourselves the trouble...
//
if (!(CurrentLength > 0)) {
break;
}
//
// Find the first free run in the supplied range.
//
AgpFindFreeRun(AgpContext,
Range,
CurrentLength,
CurrentOffset,
&RunLength,
&RunOffset);
if (RunLength > 0) {
ASSERT(RunLength <= CurrentLength);
ASSERT(RunOffset >= CurrentOffset);
ASSERT(RunOffset < CurrentOffset + CurrentLength);
ASSERT(RunOffset + RunLength <= CurrentOffset + CurrentLength);
//
// Compute the next offset and length
//
CurrentLength -= (RunOffset - CurrentOffset) + RunLength;
CurrentOffset = RunOffset + RunLength;
//
// Get an MDL from memory management big enough to map the
// requested range.
//
NewMdl = AgpLibAllocatePhysicalMemory(AgpContext, RunLength * PAGE_SIZE);
//
// This can fail in two ways, either no memory is available at all (NewMdl == NULL)
// or some pages were available, but not enough. (NewMdl->ByteCount < Length)
//
if (NewMdl == NULL) {
AGPLOG(AGP_CRITICAL,
("AgpInterfaceReserveMemory - Couldn't allocate pages for %lx bytes\n",
RunLength));
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
} else if (BYTES_TO_PAGES(NewMdl->ByteCount) < RunLength) {
AGPLOG(AGP_CRITICAL,
("AgpInterfaceCommitMemory - Only allocated enough pages for %lx of %lx bytes\n",
NewMdl->ByteCount,
RunLength));
Status = STATUS_INSUFFICIENT_RESOURCES;
MmFreePagesFromMdl(NewMdl);
break;
}
//
// Now that we have our MDL, we can map this into the specified
// range.
//
if (AgpFlushPages != NULL) {
if (!NT_SUCCESS((AgpFlushPages)(AgpContext, NewMdl))) {
Status = STATUS_INSUFFICIENT_RESOURCES;
MmFreePagesFromMdl(NewMdl);
break;
}
} else {
AgpLibFlushDcacheMdl(NewMdl);
}
Status = AgpMapMemory(AgpContext,
Range,
NewMdl,
RunOffset,
MemoryBase);
if (!NT_SUCCESS(Status)) {
AGPLOG(AGP_CRITICAL,
("AgpInterfaceCommitMemory - AgpMapMemory for Mdl %08lx in range %08lx failed %08lx\n",
NewMdl,
Range,
Status));
MmFreePagesFromMdl(NewMdl);
break;
}
Range->CommittedPages += RunLength;
//
// Add this MDL to our list of allocated MDLs for cleanup
// If we need to cleanup, we will also need to know the page offset
// so that we can unmap the memory. Stash that value in the ByteOffset
// field of the MDL (ByteOffset is always 0 for our MDLs)
//
NewMdl->ByteOffset = RunOffset;
NewMdl->Next = FirstMdl;
FirstMdl = NewMdl;
}
} while (RunLength > 0);
//
// Cleanup the MDLs. If the allocation failed, we need to
// unmap them and free the pages and the MDL itself. If the
// operation completed successfully, we just need to free the
// MDL.
//
while (FirstMdl) {
NewMdl = FirstMdl;
FirstMdl = NewMdl->Next;
if (!NT_SUCCESS(Status)) {
//
// Unmap the memory that was mapped. The ByteOffset field
// of the MDL is overloaded here to store the offset in pages
// into the range.
//
AgpUnMapMemory(AgpContext,
Range,
NewMdl->ByteCount / PAGE_SIZE,
NewMdl->ByteOffset);
NewMdl->ByteOffset = 0;
Range->CommittedPages -= NewMdl->ByteCount / PAGE_SIZE;
MmFreePagesFromMdl(NewMdl);
}
ExFreePool(NewMdl);
}
if (NT_SUCCESS(Status)) {
if (Mdl) {
//
// Get the MDL that describes the entire mapped range.
//
AgpGetMappedPages(AgpContext,
Range,
NumberOfPages,
OffsetInPages,
Mdl);
}
MemoryBase->QuadPart = Range->MemoryBase.QuadPart + OffsetInPages * PAGE_SIZE;
}
UNLOCK_MASTER(Extension);
return(Status);
}
NTSTATUS
AgpInterfaceFreeMemory(
IN PMASTER_EXTENSION Extension,
IN PVOID MapHandle,
IN ULONG NumberOfPages,
IN ULONG OffsetInPages
)
/*++
Routine Description:
Frees memory previously committed by AgpInterfaceCommitMemory
Arguments:
Extension - Supplies the device extension where physical address space should
be freed.
MapHandle - Supplies the mapping handle returned from AgpInterfaceReserveMemory
NumberOfPages - Supplies the number of pages to be freed.
OffsetInPages - Supplies the start of the range to be freed.
Return Value:
NTSTATUS
--*/
{
PAGP_RANGE Range = (PAGP_RANGE)MapHandle;
PVOID AgpContext;
NTSTATUS Status;
PMDL FreeMdl;
PAGED_CODE();
AgpContext = GET_AGP_CONTEXT_FROM_MASTER(Extension);
ASSERT(OffsetInPages < Range->NumberOfPages);
ASSERT(OffsetInPages + NumberOfPages <= Range->NumberOfPages);
//
// Make sure the supplied address is within the reserved range
//
if ((OffsetInPages >= Range->NumberOfPages) ||
(OffsetInPages + NumberOfPages > Range->NumberOfPages)) {
AGPLOG(AGP_WARNING,
("AgpInterfaceFreeMemory - Invalid free of %x pages at offset %x from range %I64X (%x pages)\n",
NumberOfPages,
OffsetInPages,
Range->MemoryBase.QuadPart,
Range->NumberOfPages));
return(STATUS_INVALID_PARAMETER);
}
//
// Allocate an MDL big enough to contain the pages to be unmapped.
//
FreeMdl = MmCreateMdl(NULL, 0, NumberOfPages * PAGE_SIZE);
if (FreeMdl == NULL) {
//
// This is kind of a sticky situation. We can't allocate the memory
// that we need to free up some memory! I guess we could have a small
// MDL on our stack and free things that way.
//
// John Vert (jvert) 11/11/1997
// implement this
//
// ISSUE-2000/09/06-enelson I've tried several AGP video cards, and
// not one of them uses AgpInterfaceCommit/FreeMemory, hence I'm
// loathe to change this at this point...
//
ASSERT(FreeMdl != NULL);
return(STATUS_INSUFFICIENT_RESOURCES);
}
LOCK_MASTER(Extension);
//
// Get the MDL that describes the entire mapped range
//
AgpGetMappedPages(AgpContext,
Range,
NumberOfPages,
OffsetInPages,
FreeMdl);
//
// Unmap the memory
//
Status = AgpUnMapMemory(AgpContext,
Range,
NumberOfPages,
OffsetInPages);
UNLOCK_MASTER(Extension);
if (!NT_SUCCESS(Status)) {
AGPLOG(AGP_CRITICAL,
("AgpInterfaceFreeMemory - UnMapMemory for %x pages at %I64X failed %08lx\n",
NumberOfPages,
Range->MemoryBase.QuadPart + OffsetInPages * PAGE_SIZE,
Status));
} else {
//
// Free the pages
//
MmFreePagesFromMdl(FreeMdl);
ASSERT(Range->CommittedPages >= NumberOfPages);
Range->CommittedPages -= NumberOfPages;
}
//
// Free the MDL we allocated.
//
ExFreePool(FreeMdl);
return(Status);
}
NTSTATUS
AgpInterfaceGetMappedPages(
IN PMASTER_EXTENSION Extension,
IN PVOID MapHandle,
IN ULONG NumberOfPages,
IN ULONG OffsetInPages,
OUT PMDL Mdl
)
/*++
Routine Description:
Returns the list of physical pages mapped backing the specified range.
Arguments:
Extension - Supplies the device extension where physical address space should
be freed.
MapHandle - Supplies the mapping handle returned from AgpInterfaceReserveMemory
NumberOfPages - Supplies the number of pages to be returned
OffsetInPages - Supplies the start of the rangion
Return Value:
NTSTATUS
--*/
{
PAGP_RANGE Range = (PAGP_RANGE)MapHandle;
PVOID AgpContext;
NTSTATUS Status;
PAGED_CODE();
AgpContext = GET_AGP_CONTEXT_FROM_MASTER(Extension);
ASSERT(NumberOfPages <= Range->NumberOfPages);
ASSERT(NumberOfPages > 0);
ASSERT(OffsetInPages < Range->NumberOfPages);
ASSERT(OffsetInPages + NumberOfPages <= Range->NumberOfPages);
ASSERT(Mdl->ByteCount == PAGE_SIZE * NumberOfPages);
//
// Make sure the supplied address is within the reserved range
//
if ((OffsetInPages >= Range->NumberOfPages) ||
(OffsetInPages + NumberOfPages > Range->NumberOfPages)) {
AGPLOG(AGP_WARNING,
("AgpInterfaceGetMappedPages - Invalid 'get' of %x pages at offset %x from range %I64X (%x pages)\n",
NumberOfPages,
OffsetInPages,
Range->MemoryBase.QuadPart,
Range->NumberOfPages));
return(STATUS_INVALID_PARAMETER);
}
//
// Get the MDL that describes the entire mapped range
//
LOCK_MASTER(Extension);
AgpGetMappedPages(AgpContext,
Range,
NumberOfPages,
OffsetInPages,
Mdl);
UNLOCK_MASTER(Extension);
return(STATUS_SUCCESS);
}
PMDL
AgpLibAllocatePhysicalMemory(IN PVOID AgpContext, IN ULONG TotalBytes)
/*++
Routine Description:
Allocates a set of physical memory pages for use by the AGP driver.
This routine uses MmAllocatePagesForMdl to attempt to allocate
as many of the pages as possible within favored AGP memory
ranges (if any).
Arguments:
AgpContext - The AgpContext
TotalBytes - The total amount of bytes to allocate.
Return Value:
An MDL that describes the allocated physical pages or NULL
if this function is unsuccessful.
NOTE: Just like MmAllocatePagesForMdl, this function can return
an MDL that describes an allocation smaller than TotalBytes in size.
--*/
{
PHYSICAL_ADDRESS ZeroAddress, MaxAddress;
PMDL MdlList = NULL, NewMdl = NULL;
PTARGET_EXTENSION Extension;
ULONG i, PagesNeeded;
PAGED_CODE();
AGPLOG(AGP_NOISE, ("AGPLIB: Attempting to allocate memory = %u pages.\n",
BYTES_TO_PAGES(TotalBytes)));
// Initialize some stuff
ZeroAddress.QuadPart = 0;
MAX_MEM(MaxAddress.QuadPart);
AGPLOG(AGP_NOISE, ("AGPLIB: Max memory set to %I64x.\n", MaxAddress.QuadPart));
GET_TARGET_EXTENSION(Extension, AgpContext);
// How many pages do we need?
PagesNeeded = BYTES_TO_PAGES(TotalBytes);
//
// Loop through each favored range, attempting to allocate
// as much as possible from that range within the bounds
// of what we actually need.
//
for (i = 0; i < Extension->FavoredMemory.NumRanges; i++) {
AGPLOG(AGP_NOISE,
("AGPLIB: Trying to allocate %u pages from range %I64x - %I64x.\n",
PagesNeeded,
Extension->FavoredMemory.Ranges[i].Lower,
Extension->FavoredMemory.Ranges[i].Upper));
NewMdl = MmAllocatePagesForMdl(Extension->FavoredMemory.Ranges[i].Lower,
Extension->FavoredMemory.Ranges[i].Upper,
ZeroAddress,
PagesNeeded << PAGE_SHIFT);
if (NewMdl) {
AGPLOG(AGP_NOISE, ("AGPLIB: %u pages allocated in range.\n",
NewMdl->ByteCount >> PAGE_SHIFT));
PagesNeeded -= (NewMdl->ByteCount >> PAGE_SHIFT);
//
// Build a list of the MDls used
// for each range-based allocation
//
NewMdl->Next = MdlList;
MdlList = NewMdl;
// Stop allocating if we are finished.
if (PagesNeeded == 0) break;
} else {
AGPLOG(AGP_NOISE, ("AGPLIB: NO pages allocated in range.\n"));
}
}
//
// Attempt to allocate from ALL of physical memory
// if we could not complete our allocation with only
// the favored memory ranges.
//
if (PagesNeeded > 0) {
AGPLOG(AGP_NOISE, ("AGPLIB: Global Memory allocation for %u pages.\n",
PagesNeeded));
NewMdl = MmAllocatePagesForMdl(ZeroAddress,
MaxAddress,
ZeroAddress,
PagesNeeded << PAGE_SHIFT);
if (NewMdl) {
AGPLOG(AGP_NOISE, ("AGPLIB: Good Global Memory Alloc for %u pages.\n",
NewMdl->ByteCount >> PAGE_SHIFT));
//
// Add this MDL to the list as well
//
NewMdl->Next = MdlList;
MdlList = NewMdl;
} else {
AGPLOG(AGP_NOISE, ("AGPLIB: Failed Global Memory Alloc.\n"));
}
}
// We now have a list of Mdls in MdlList that give us the best
// possible memory allocation taking favored ranges into account.
// What we now need to do is combine this Mdl list into one mdl.
NewMdl = AgpCombineMdlList(MdlList);
if (!NewMdl && MdlList) {
AGPLOG(AGP_WARNING, ("AGPLIB: Could not combine MDL List!\n"));
// This is bad. The mdl list could not be combined probably
// because a large enough mdl could not be allocated for
// the combination.
// This is not the end of the world however, since the mdl list
// is not modified until its combination has succeeded so we
// still have a valid list. But we need it in one Mdl, so
// we just fall back to the simplest allocation strategy
// we have available:
// 1. Destroy the list and all of its allocations.
while(MdlList)
{
MmFreePagesFromMdl(MdlList);
NewMdl = MdlList->Next;
ExFreePool(MdlList);
MdlList = NewMdl;
}
// 2. Allocate a single Mdl with our pages without regard
// for favored memory ranges.
NewMdl = MmAllocatePagesForMdl(ZeroAddress,
MaxAddress,
ZeroAddress,
TotalBytes);
}
return NewMdl;
}
PMDL
AgpCombineMdlList(IN PMDL MdlList)
/*++
Routine Description:
Combines a list of MDLs that describe some set of physical memory
pages into a single MDL that describes the same set of pages.
The MDLs in the list should be of the type produced by
MmAllocatePagesForMdl (i.e. MDLs that are useful for nothing more
than as an array of PFNs)
This function is used by AgpLibAllocatePhysicalMemory in order
to combine its multiple range-based allocations into 1 MDL.
Arguments:
MdlList - A list of MDLs to be combines
Return Value:
A single MDL that describes the same set of physical pages as
the MDLs in MdlList or NULL if this function is unsuccessful.
NOTE: This function will deallocate the Mdls in MdlList if it
is successful. If it is unsuccessful, however, it will leave
the MdlList intact.
--*/
{
PMDL NewMdl = NULL, Mdl, MdlTemp;
ULONG Pages = 0;
PPFN_NUMBER NewPageArray, PageArray;
ULONG i; // for debugging only
PAGED_CODE();
if ((MdlList == NULL) || (MdlList->Next == NULL)) {
// List of 0 or 1 elements, no need for this
// function to do anything.
return MdlList;
}
// Calculate the number of pages spanned by this MdlList.
for(Mdl = MdlList; Mdl; Mdl = Mdl->Next)
Pages += BYTES_TO_PAGES(Mdl->ByteCount);
// Allocate a new Mdl of the proper size.
NewMdl = MmCreateMdl(NULL, NULL, Pages << PAGE_SHIFT);
if (!NewMdl) {
// Chances are that the system will bugcheck before
// this actually happens ... but whatever.
return NULL;
}
// Run through the mdl list, combining the mdls found
// into a new mdl.
//
// First, get a pointer to the PFN array of the new Mdl
//
NewPageArray = MmGetMdlPfnArray(NewMdl);
for(Mdl = MdlList; Mdl; Mdl = Mdl->Next)
{
// Get a pointer to the physical page number array in this Mdl.
PageArray = MmGetMdlPfnArray(Mdl);
Pages = Mdl->ByteCount >> PAGE_SHIFT;
// Copy this array into a proper slot in the array area of the new Mdl.
RtlCopyMemory((PVOID)NewPageArray,
(PVOID)PageArray,
sizeof(PFN_NUMBER) * Pages);
// Adjust new array slot pointer appropriately for the next copy
NewPageArray += Pages;
}
// The list has been combined, now we need to destroy the Mdls
// in the list.
Mdl = MdlList;
while(Mdl)
{
MdlTemp = Mdl->Next;
ExFreePool(Mdl);
Mdl = MdlTemp;
}
// All done. Return the new combined Mdl.
return NewMdl;
}
PVOID
AgpLibAllocateMappedPhysicalMemory(IN PVOID AgpContext, IN ULONG TotalBytes)
/*++
Routine Description:
Same as AgpLibAllocatePhysicalMemory, except this function will
also map the allocated memory to a virtual address.
Arguments:
Same as AgpLibAllocatePhysicalMemory.
Return Value:
A virtual address of the allocated memory or NULL if unsuccessful.
--*/
{
PMDL Mdl;
PVOID Ret;
PAGED_CODE();
AGPLOG(AGP_NOISE,
("AGPLIB: Attempting to allocate mapped memory = %u.\n", TotalBytes));
//
// Call the real memory allocator.
//
Mdl = AgpLibAllocatePhysicalMemory(AgpContext, TotalBytes);
// Two possible failures
// 1. MDL is NULL. No memory could be allocated.
if (Mdl == NULL) {
AGPLOG(AGP_WARNING, ("AGPMAP: Could not allocate anything.\n"));
return NULL;
}
// 2. MDL has some pages allocated but not enough.
if (Mdl->ByteCount < TotalBytes) {
AGPLOG(AGP_WARNING, ("AGPMAP: Could not allocate enough.\n"));
MmFreePagesFromMdl(Mdl);
ExFreePool(Mdl);
return NULL;
}
// Ok. Our allocation succeeded. Map it to a virtual address.
// Step 1: Map the locked Pages. (will return NULL if failed)
Mdl->MdlFlags |= MDL_PAGES_LOCKED;
Ret = MmMapLockedPagesSpecifyCache (Mdl,
KernelMode,
MmNonCached,
NULL,
FALSE,
HighPagePriority);
// Don't need the Mdl anymore, whether we succeeded or failed.
ExFreePool(Mdl);
if (Ret == NULL) {
AGPLOG(AGP_WARNING, ("AGPMAP: Could not map.\n"));
}
return Ret;
}
#if defined (_X86_)
#define FLUSH_DCACHE(Mdl) __asm{ wbinvd }
#else
#define FLUSH_DCACHE(Mdl) \
AGPLOG(AGP_CRITICAL, \
("AgpLibFlushDcacheMdl - NEED TO IMPLEMENT DCACHE FLUSH FOR THIS ARCHITECTURE!!\n"))
#endif
VOID
AgpLibFlushDcacheMdl(
PMDL Mdl
)
/*++
Routine Description:
Flushes the specified MDL from the D-caches of all processors
in the system.
Current algorithm is to set the current thread's affinity to each
processor in turn and flush the dcache. This could be made a lot
more efficient if this turns out to be a hot codepath
Arguments:
Mdl - Supplies the MDL to be flushed.
Return Value:
None.
--*/
{
NTSTATUS Status;
KAFFINITY Processors;
UCHAR Number;
KEVENT Event;
KDPC Dpc;
PAGED_CODE();
Processors = KeQueryActiveProcessors();
//
// Quick out for the UP case.
//
if (Processors == 1) {
FLUSH_DCACHE(Mdl);
return;
}
//
// We will invoke a DPC on each processor. That DPC will flush the cache,
// set the event and return.
//
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Number = 0;
while (Processors) {
if (Processors & 1) {
//
// Initialize the DPC and set it to run on the specified
// processor.
//
KeInitializeDpc(&Dpc,ApFlushDcache, &Event);
KeSetTargetProcessorDpc(&Dpc, Number);
//
// Queue the DPC and wait for it to finish its work.
//
KeClearEvent(&Event);
KeInsertQueueDpc(&Dpc, Mdl, NULL);
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
}
Processors = Processors >> 1;
++Number;
}
}
VOID
ApFlushDcache(
IN PKDPC Dpc,
IN PKEVENT Event,
IN PMDL Mdl,
IN PVOID SystemArgument2
)
/*++
Routine Description:
DPC which executes on each processor in turn to flush the
specified MDL out of the dcache on each.
Arguments:
Dpc - supplies the DPC object
Event - Supplies the event to signal when the DPC is complete
Mdl - Supplies the MDL to be flushed from the dcache
Return Value:
None
--*/
{
FLUSH_DCACHE(Mdl);
KeSetEvent(Event, 0, FALSE);
}
NTSTATUS
AgpInterfaceSetRate(
IN PMASTER_EXTENSION Extension,
IN ULONG AgpRate
)
/*++
Routine Description:
This routine sets the AGP rate
Arguments:
Extension - Supplies the device extension
AgpRate - Rate to set
Return Value:
STATUS_SUCCESS, or error status
--*/
{
ULONGLONG DeviceFlags = 0;
PAGED_CODE();
switch (AgpRate) {
case PCI_AGP_RATE_1X:
DeviceFlags = AGP_FLAG_SET_RATE_1X;
break;
case PCI_AGP_RATE_2X:
DeviceFlags = AGP_FLAG_SET_RATE_2X;
break;
case PCI_AGP_RATE_4X:
DeviceFlags = AGP_FLAG_SET_RATE_4X;
break;
case 8:
DeviceFlags = AGP_FLAG_SET_RATE_8X;
break;
}
if (DeviceFlags != 0) {
return AgpSpecialTarget(GET_AGP_CONTEXT_FROM_MASTER(Extension),
DeviceFlags);
}
return STATUS_INVALID_PARAMETER;
}