/*++ 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 phyicially contiguous data buffers for DMA transfers. // Map registers are used 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. // PADAPTER_OBJECT MasterAdapterObject; BOOLEAN LessThan16Mb; BOOLEAN HalpEisaDma; // // Map buffer prameters. These are initialized in HalInitSystem // PHYSICAL_ADDRESS HalpMapBufferPhysicalAddress; ULONG HalpMapBufferSize; 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 physicalAddress; KIRQL Irql; PVOID CodeLockHandle; PAGED_CODE(); NumberOfPages = BYTES_TO_PAGES(Amount); // // Make sure there is room for the addition pages. The maximum number of // slots needed is equal to NumberOfPages + Amount / 64K + 1. // i = BYTES_TO_PAGES(MAXIMUM_MAP_BUFFER_SIZE) - (NumberOfPages + (NumberOfPages * PAGE_SIZE) / 0x10000 + 1 + AdapterObject->NumberOfMapRegisters); if (i < 0) { // // Reduce the allocatation amount to so it will fit. // NumberOfPages += i; } if (NumberOfPages <= 0) { // // No more memory can be allocated. // return(FALSE); } if (AdapterObject->NumberOfMapRegisters == 0 && HalpMapBufferSize) { NumberOfPages = BYTES_TO_PAGES(HalpMapBufferSize); // // Since this is the initial allocation, use the buffer allocated by // HalInitSystem rather than allocationg a new one. // MapBufferPhysicalAddress = HalpMapBufferPhysicalAddress.LowPart; // // Map the buffer for access. // MapBufferVirtualAddress = MmMapIoSpace( HalpMapBufferPhysicalAddress, HalpMapBufferSize, TRUE // Cache enable. ); if (MapBufferVirtualAddress == NULL) { // // The buffer could not be mapped. // HalpMapBufferSize = 0; return(FALSE); } } else { // // Allocate the map buffers. // physicalAddress.LowPart = MAXIMUM_PHYSICAL_ADDRESS - 1; physicalAddress.HighPart = 0; MapBufferVirtualAddress = MmAllocateContiguousMemory( NumberOfPages * PAGE_SIZE, physicalAddress ); if (MapBufferVirtualAddress == NULL) { return(FALSE); } // // Get the physical address of the map base. // MapBufferPhysicalAddress = MmGetPhysicalAddress( MapBufferVirtualAddress ).LowPart; } // // Initailize the map registers where memory has been allocated. // Serialize with master adapter object // CodeLockHandle = MmLockPagableCodeSection (&HalpGrowMapBuffers); Irql = KfAcquireSpinLock( &AdapterObject->SpinLock ); 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, 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 where allocated. // AdapterObject->NumberOfMapRegisters += NumberOfPages; // // Release master adapter object // KfReleaseSpinLock( &AdapterObject->SpinLock, Irql ); MmUnlockPagableImageSection (CodeLockHandle); return(TRUE); } 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 phyically 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. ChannelNumber - Unused. 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; UNREFERENCED_PARAMETER(ChannelNumber); PAGED_CODE(); // // Initalize the master adapter if necessary. // if (MasterAdapterObject == NULL && AdapterBaseVa != (PVOID) -1 && MapRegistersPerChannel) { MasterAdapterObject = HalpAllocateAdapter( MapRegistersPerChannel, (PVOID) -1, NULL ); // // If we could not allocate the master adapter then give up. // if (MasterAdapterObject == NULL) { return(NULL); } } // // Begin by initializing the object attributes structure to be used when // creating the adapter object. // InitializeObjectAttributes( &ObjectAttributes, NULL, OBJ_PERMANENT, (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 (AdapterBaseVa == (PVOID) -1) { // // Allocate a bit map large enough MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE // of map register buffers. // BitmapSize = (((sizeof( RTL_BITMAP ) + (( MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE ) + 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 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->Type = IO_TYPE_ADAPTER; AdapterObject->Size = (USHORT) Size; AdapterObject->MapRegistersPerChannel = 1; AdapterObject->AdapterBaseVa = AdapterBaseVa; if (MapRegistersPerChannel) { AdapterObject->MasterAdapter = MasterAdapterObject; } 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 ( AdapterBaseVa == (PVOID) -1 ) { KeInitializeSpinLock( &AdapterObject->SpinLock ); InitializeListHead( &AdapterObject->AdapterQueue ); AdapterObject->MapRegisters = (PVOID) ( AdapterObject + 1); RtlInitializeBitMap( AdapterObject->MapRegisters, (PULONG) (((PCHAR) (AdapterObject->MapRegisters)) + sizeof( RTL_BITMAP )), ( MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE ) ); // // 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 = ExAllocatePool( NonPagedPool, (MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE) * sizeof(TRANSLATION_ENTRY) ); if (AdapterObject->MapRegisterBase == NULL) { ObDereferenceObject( AdapterObject ); AdapterObject = NULL; return(NULL); } // // Zero the map registers. // RtlZeroMemory( AdapterObject->MapRegisterBase, (MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE) * 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; }