|
|
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
ixphwsup.c
Abstract:
This module contains the HalpXxx routines for the NT I/O system that are hardware dependent. Were these routines not hardware dependent, they would normally reside in the internal.c module.
Author:
Darryl E. Havens (darrylh) 11-Apr-1990
Environment:
Kernel mode, local to I/O system
Revision History:
--*/
#include "halp.h"
#if MCA
#include "mca.h"
#else
#include "eisa.h"
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,HalpAllocateAdapter)
#pragma alloc_text(PAGELK,HalpGrowMapBuffers)
#endif
//
// Some devices require a physically contiguous data buffer for DMA transfers.
// Map registers are used to give the appearance that all data buffers are
// contiguous. In order to pool all of the map registers a master
// adapter object is used. This object is allocated and saved internal to this
// file. It contains a bit map for allocation of the registers and a queue
// for requests which are waiting for more map registers. This object is
// allocated during the first request to allocate an adapter which requires
// map registers.
//
#if defined(_HALPAE_)
MASTER_ADAPTER_OBJECT MasterAdapter24; MASTER_ADAPTER_OBJECT MasterAdapter32; #else
PADAPTER_OBJECT MasterAdapterObject; #endif
BOOLEAN LessThan16Mb; BOOLEAN HalpEisaDma;
#define ADAPTER_BASE_MASTER ((PVOID)-1)
#if !defined(_HALPAE_)
//
// Map buffer prameters. These are initialized in HalInitSystem.
//
PHYSICAL_ADDRESS HalpMapBufferPhysicalAddress; ULONG HalpMapBufferSize;
#endif
//
// Define DMA operations structure.
//
const DMA_OPERATIONS HalpDmaOperations = { sizeof(DMA_OPERATIONS), (PPUT_DMA_ADAPTER) HalPutDmaAdapter, (PALLOCATE_COMMON_BUFFER) HalAllocateCommonBuffer, (PFREE_COMMON_BUFFER) HalFreeCommonBuffer, #if defined(_WIN64)
(PALLOCATE_ADAPTER_CHANNEL) HalRealAllocateAdapterChannel, #else
(PALLOCATE_ADAPTER_CHANNEL) IoAllocateAdapterChannel, #endif
(PFLUSH_ADAPTER_BUFFERS) IoFlushAdapterBuffers, (PFREE_ADAPTER_CHANNEL) IoFreeAdapterChannel, (PFREE_MAP_REGISTERS) IoFreeMapRegisters, (PMAP_TRANSFER) IoMapTransfer, (PGET_DMA_ALIGNMENT) HalGetDmaAlignment, (PREAD_DMA_COUNTER) HalReadDmaCounter, (PGET_SCATTER_GATHER_LIST) HalGetScatterGatherList, (PPUT_SCATTER_GATHER_LIST) HalPutScatterGatherList, (PCALCULATE_SCATTER_GATHER_LIST_SIZE)HalCalculateScatterGatherListSize, (PBUILD_SCATTER_GATHER_LIST) HalBuildScatterGatherList, (PBUILD_MDL_FROM_SCATTER_GATHER_LIST) HalBuildMdlFromScatterGatherList };
BOOLEAN HalpGrowMapBuffers( PADAPTER_OBJECT AdapterObject, ULONG Amount ) /*++
Routine Description:
This function attempts to allocate additional map buffers for use by I/O devices. The map register table is updated to indicate the additional buffers.
Caller owns the HalpNewAdapter event
Arguments:
AdapterObject - Supplies the adapter object for which the buffers are to be allocated.
Amount - Indicates the size of the map buffers which should be allocated.
Return Value:
TRUE is returned if the memory could be allocated.
FALSE is returned if the memory could not be allocated.
--*/ { ULONG MapBufferPhysicalAddress; PVOID MapBufferVirtualAddress; PTRANSLATION_ENTRY TranslationEntry; LONG NumberOfPages; LONG i; PHYSICAL_ADDRESS physicalAddressMinimum; PHYSICAL_ADDRESS physicalAddressMaximum; PHYSICAL_ADDRESS boundaryAddress; KIRQL Irql; PVOID CodeLockHandle; ULONG maximumBufferPages; BOOLEAN dma32Bit; ULONG bytesToAllocate;
PAGED_CODE();
dma32Bit = AdapterObject->Dma32BitAddresses; boundaryAddress.QuadPart = 0;
NumberOfPages = BYTES_TO_PAGES(Amount);
//
// Make sure there is room for the additional pages. The maximum number of
// slots needed is equal to NumberOfPages + Amount / 64K + 1.
//
maximumBufferPages = HalpMaximumMapBufferRegisters( dma32Bit );
i = maximumBufferPages - (NumberOfPages + (NumberOfPages * PAGE_SIZE) / 0x10000 + 1 + AdapterObject->NumberOfMapRegisters);
if (i < 0) {
//
// Reduce the allocation amount so it will fit.
//
NumberOfPages += i; }
if (NumberOfPages <= 0) {
//
// No more memory can be allocated.
//
return(FALSE); }
if (AdapterObject->NumberOfMapRegisters == 0 && HalpMapBufferSize( dma32Bit )) {
NumberOfPages = BYTES_TO_PAGES( HalpMapBufferSize( dma32Bit ));
//
// Since this is the initial allocation, use the buffer allocated by
// HalInitSystem rather than allocating a new one.
//
MapBufferPhysicalAddress = HalpMapBufferPhysicalAddress( dma32Bit ).LowPart;
//
// Map the buffer for access.
//
MapBufferVirtualAddress = MmMapIoSpace( HalpMapBufferPhysicalAddress( dma32Bit ), HalpMapBufferSize( dma32Bit ), TRUE // Cache enable.
);
if (MapBufferVirtualAddress == NULL) {
//
// The buffer could not be mapped.
//
HalpMapBufferSize( dma32Bit ) = 0; return(FALSE); }
} else {
//
// Allocate the map buffers.
//
physicalAddressMaximum = HalpGetAdapterMaximumPhysicalAddress( AdapterObject );
if (physicalAddressMaximum.LowPart == (ULONG)-1) {
//
// This adapter can handle at least 32-bit addresses. In an effort
// to leave memory to 24-bit adapters, try to make this allocation
// above the 24 bit line.
//
physicalAddressMinimum.QuadPart = MAXIMUM_PHYSICAL_ADDRESS;
} else {
physicalAddressMinimum.QuadPart = 0; }
bytesToAllocate = NumberOfPages * PAGE_SIZE;
//
// This loop is executed a maximum of two times.
//
while(TRUE) {
MapBufferVirtualAddress = MmAllocateContiguousMemorySpecifyCache( bytesToAllocate, physicalAddressMinimum, physicalAddressMaximum, boundaryAddress, MmCached );
if (MapBufferVirtualAddress != NULL) {
//
// The memory was allocated.
//
break; }
//
// The allocation attempt failed.
//
if (physicalAddressMinimum.QuadPart != 0) {
//
// We were trying to allocate memory above the 16M line as
// an optimization. Relax that requirement and try again.
//
physicalAddressMinimum.QuadPart = 0;
} else {
//
// The memory could not be allocated.
//
return FALSE; } }
//
// Get the physical address of the map base.
//
MapBufferPhysicalAddress = MmGetPhysicalAddress( MapBufferVirtualAddress ).LowPart;
}
//
// Initialize the map registers where memory has been allocated.
// Serialize with master adapter object.
//
CodeLockHandle = MmLockPagableCodeSection (&HalpGrowMapBuffers); KeAcquireSpinLock( &AdapterObject->SpinLock, &Irql );
TranslationEntry = ((PTRANSLATION_ENTRY) AdapterObject->MapRegisterBase) + AdapterObject->NumberOfMapRegisters;
for (i = 0; (LONG) i < NumberOfPages; i++) {
//
// Make sure the perivous entry is physically contiguous with the next
// entry and that a 64K physical bountry is not crossed unless this
// is an Eisa system.
//
if (TranslationEntry != AdapterObject->MapRegisterBase && (((TranslationEntry - 1)->PhysicalAddress + PAGE_SIZE) != MapBufferPhysicalAddress || (!HalpEisaDma && ((TranslationEntry - 1)->PhysicalAddress & ~0x0ffff) != (MapBufferPhysicalAddress & ~0x0ffff)))) {
//
// An entry needs to be skipped in the table. This entry will
// remain marked as allocated so that no allocation of map
// registers will cross this bountry.
//
TranslationEntry++; AdapterObject->NumberOfMapRegisters++; }
//
// Clear the bits where the memory has been allocated.
//
RtlClearBits( AdapterObject->MapRegisters, (ULONG)(TranslationEntry - (PTRANSLATION_ENTRY) AdapterObject->MapRegisterBase), 1 );
TranslationEntry->VirtualAddress = MapBufferVirtualAddress; TranslationEntry->PhysicalAddress = MapBufferPhysicalAddress; TranslationEntry++; (PCCHAR) MapBufferVirtualAddress += PAGE_SIZE; MapBufferPhysicalAddress += PAGE_SIZE;
}
//
// Remember the number of pages that were allocated.
//
AdapterObject->NumberOfMapRegisters += NumberOfPages;
//
// Release master adapter object.
//
KeReleaseSpinLock( &AdapterObject->SpinLock, Irql ); MmUnlockPagableImageSection (CodeLockHandle); return(TRUE); }
#if defined(HalpAllocateAdapterEx)
#undef HalpAllocateAdapterEx
#endif
PADAPTER_OBJECT HalpAllocateAdapterEx( IN ULONG MapRegistersPerChannel, IN PVOID AdapterBaseVa, IN PVOID ChannelNumber, IN BOOLEAN Dma32Bit )
/*++
Routine Description:
This routine allocates and initializes an adapter object to represent an adapter or a DMA controller on the system. If no map registers are required then a standalone adapter object is allocated with no master adapter.
If map registers are required, then a master adapter object is used to allocate the map registers. For Isa systems these registers are really physically contiguous memory pages.
Caller owns the HalpNewAdapter event
Arguments:
MapRegistersPerChannel - Specifies the number of map registers that each channel provides for I/O memory mapping.
AdapterBaseVa - Address of the the DMA controller or ADAPTER_BASE_MASTER.
ChannelNumber - Unused.
Dma32Bit - Indicates whether the adapter is 24 bit or 32 bit.
Return Value:
The function value is a pointer to the allocate adapter object.
--*/
{
PADAPTER_OBJECT AdapterObject; OBJECT_ATTRIBUTES ObjectAttributes; ULONG Size; ULONG BitmapSize; HANDLE Handle; NTSTATUS Status; ULONG mapBuffers; BOOLEAN creatingMaster;
UNREFERENCED_PARAMETER(ChannelNumber);
PAGED_CODE();
if (AdapterBaseVa == ADAPTER_BASE_MASTER) { creatingMaster = TRUE; } else { creatingMaster = FALSE; }
//
// Initialize the master adapter if necessary.
//
if (creatingMaster == FALSE && MapRegistersPerChannel != 0) {
//
// This is not a recursive master adapter allocation, and map registers
// are necessary. Allocate a master adapter object of the appropriate
// type if necessary.
//
if (HalpMasterAdapter( Dma32Bit ) == NULL) {
AdapterObject = HalpAllocateAdapterEx( MapRegistersPerChannel, ADAPTER_BASE_MASTER, NULL, Dma32Bit );
//
// If we could not allocate the master adapter then give up.
//
if (AdapterObject == NULL) { return NULL; }
AdapterObject->Dma32BitAddresses = Dma32Bit; AdapterObject->MasterDevice = Dma32Bit; HalpMasterAdapter( Dma32Bit ) = AdapterObject; } }
//
// Begin by initializing the object attributes structure to be used when
// creating the adapter object.
//
InitializeObjectAttributes( &ObjectAttributes, NULL, OBJ_PERMANENT | OBJ_KERNEL_HANDLE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL );
//
// Determine the size of the adapter object. If this is the master object
// then allocate space for the register bit map; otherwise, just allocate
// an adapter object.
//
if (creatingMaster != FALSE) {
//
// Allocate a bit map large enough MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE
// of map register buffers.
//
mapBuffers = HalpMaximumMapBufferRegisters( Dma32Bit );
BitmapSize = (((sizeof( RTL_BITMAP ) + ((mapBuffers + 7) >> 3)) + 3) & ~3);
Size = sizeof( ADAPTER_OBJECT ) + BitmapSize;
} else {
Size = sizeof( ADAPTER_OBJECT );
}
//
// Now create the adapter object.
//
Status = ObCreateObject( KernelMode, *IoAdapterObjectType, &ObjectAttributes, KernelMode, (PVOID) NULL, Size, 0, 0, (PVOID *)&AdapterObject );
//
// Reference the object.
//
if (NT_SUCCESS(Status)) {
Status = ObReferenceObjectByPointer( AdapterObject, FILE_READ_DATA | FILE_WRITE_DATA, *IoAdapterObjectType, KernelMode );
}
//
// If the adapter object was successfully created, then attempt to insert
// it into the object table.
//
if (NT_SUCCESS( Status )) {
RtlZeroMemory (AdapterObject, sizeof (ADAPTER_OBJECT));
Status = ObInsertObject( AdapterObject, NULL, FILE_READ_DATA | FILE_WRITE_DATA, 0, (PVOID *) NULL, &Handle );
if (NT_SUCCESS( Status )) {
ZwClose( Handle );
//
// Initialize the adapter object itself.
//
AdapterObject->DmaHeader.Version = IO_TYPE_ADAPTER; AdapterObject->DmaHeader.Size = (USHORT) Size; AdapterObject->MapRegistersPerChannel = 1; AdapterObject->AdapterBaseVa = AdapterBaseVa; AdapterObject->ChannelNumber = 0xff; AdapterObject->DmaHeader.DmaOperations = (PDMA_OPERATIONS)&HalpDmaOperations; AdapterObject->Dma32BitAddresses = Dma32Bit;
if (MapRegistersPerChannel) {
AdapterObject->MasterAdapter = HalpMasterAdapter( Dma32Bit );
} else {
AdapterObject->MasterAdapter = NULL;
}
//
// Initialize the channel wait queue for this adapter.
//
KeInitializeDeviceQueue( &AdapterObject->ChannelWaitQueue );
//
// If this is the MasterAdatper then initialize the register bit map,
// AdapterQueue and the spin lock.
//
if (creatingMaster != FALSE) {
KeInitializeSpinLock( &AdapterObject->SpinLock );
InitializeListHead( &AdapterObject->AdapterQueue );
AdapterObject->MapRegisters = (PVOID) ( AdapterObject + 1);
RtlInitializeBitMap( AdapterObject->MapRegisters, (PULONG) (((PCHAR) (AdapterObject->MapRegisters)) + sizeof( RTL_BITMAP )), ( mapBuffers ) ); //
// Set all the bits in the memory to indicate that memory
// has not been allocated for the map buffers.
//
RtlSetAllBits( AdapterObject->MapRegisters ); AdapterObject->NumberOfMapRegisters = 0; AdapterObject->CommittedMapRegisters = 0;
//
// Allocate the memory map registers.
//
AdapterObject->MapRegisterBase = ExAllocatePoolWithTag( NonPagedPool, mapBuffers * sizeof(TRANSLATION_ENTRY), HAL_POOL_TAG );
if (AdapterObject->MapRegisterBase == NULL) {
ObDereferenceObject( AdapterObject ); AdapterObject = NULL; return(NULL);
}
//
// Zero the map registers.
//
RtlZeroMemory( AdapterObject->MapRegisterBase, mapBuffers * sizeof(TRANSLATION_ENTRY) );
if (!HalpGrowMapBuffers(AdapterObject, INITIAL_MAP_BUFFER_SMALL_SIZE)) {
//
// If no map registers could be allocated then free the
// object.
//
ObDereferenceObject( AdapterObject ); AdapterObject = NULL; return(NULL);
} }
} else {
//
// An error was incurred for some reason. Set the return value
// to NULL.
//
AdapterObject = (PADAPTER_OBJECT) NULL; } } else {
AdapterObject = (PADAPTER_OBJECT) NULL;
}
return AdapterObject;
}
PADAPTER_OBJECT HalpAllocateAdapter( IN ULONG MapRegistersPerChannel, IN PVOID AdapterBaseVa, IN PVOID ChannelNumber )
/*++
Routine Description:
This routine allocates and initializes an adapter object to represent an adapter or a DMA controller on the system. If no map registers are required then a standalone adapter object is allocated with no master adapter.
If map registers are required, then a master adapter object is used to allocate the map registers. For Isa systems these registers are really physically contiguous memory pages.
Caller owns the HalpNewAdapter event
Arguments:
MapRegistersPerChannel - Specifies the number of map registers that each channel provides for I/O memory mapping.
AdapterBaseVa - Address of the DMA controller or ADAPTER_BASE_MASTER.
ChannelNumber - Unused.
Return Value:
The function value is a pointer to the allocate adapter object.
--*/
{ return HalpAllocateAdapterEx( MapRegistersPerChannel, AdapterBaseVa, ChannelNumber, FALSE ); }
ULONG HalGetDmaAlignment ( PVOID Conext ) { return HalGetDmaAlignmentRequirement(); }
|