/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   dxbkme.c

Abstract:

   This module contains handlers for KME read and write requests.

Author:

   Michael Zarns

-*/

#include "digidxb.h"

NTSTATUS
DxbKmeRead(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp )
/*++

Description:

	This routine processes a KME read request.

Arguments:

	pExt
		The device extension identifying the adapter

	pIrp
		The IRP containing the request

Return Value:

   STATUS_SUCCESS or fail status
   
--*/
{
	const PKME_READ pRead = pIrp->AssociatedIrp.SystemBuffer;
	NTSTATUS Status;

	// Don't do zero-length reads!
	if ( pRead->Length == 0 )
	{
		Status = STATUS_INVALID_PARAMETER;
		goto Exit;
	}

	Status = DigiLockBuffer( pIrp, pRead->pBuffer, pRead->Length, &pRead->pLockedBuffer );
	if ( NT_ERROR( Status ) )
		goto Exit;

	switch ( pRead->Flags )
	{
		case KME_FLAG_ADAPTER_MEMORY:

			if ( pRead->Address >= pExt->Hardware.Memory.Length )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			pRead->BytesRead = pRead->Address + pRead->Length < pExt->Hardware.Memory.Length ?
			                   pRead->Length : pExt->Hardware.Memory.Length - pRead->Address;

			DxbAdapterRead( pExt, pRead->pLockedBuffer, pRead->Address, pRead->BytesRead );
			Status = STATUS_SUCCESS;
			break;

		case KME_FLAG_HOST_MEMORY:

			// Read kernel memory
			// Cheap hack for validating the address... kernel drivers should all be 
			// above the 2Gig virtual address.  
			if ( pRead->Address < 0x80000000 )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			RtlCopyMemory( (PVOID)pRead->Address, pRead->pLockedBuffer, pRead->Length );
		   pRead->BytesRead = pRead->Length;
			Status = STATUS_SUCCESS;
			break;

		case KME_FLAG_PCI_INTERFACE_CHIP:

			if ( pExt->Hardware.BusType != PCIBus )
			{
				Status = STATUS_INVALID_PARAMETER_4;
				break;
			}

			if ( pRead->Address >= pExt->Hardware.PciInterfaceChip.Length )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			pRead->BytesRead = pRead->Address + pRead->Length < pExt->Hardware.PciInterfaceChip.Length ?
			                   pRead->Length : pExt->Hardware.PciInterfaceChip.Length - pRead->Address;

//------------------------------------------------------------ MLZ 6/19/98 {
// TODO: Modify DxbAdapterRead to support other memory windows!

			if ( pRead->BytesRead == sizeof(ULONG) && IsDwordAligned( pRead->Address ) )
			{
				// It's a dword, write it as such
				*(PULONG)pRead->pLockedBuffer = READ_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + pRead->Address ) );
			}
			else if ( pRead->BytesRead == sizeof(USHORT) && IsWordAligned( pRead->Address ) )
			{
				// It's a word, write it as such
				*(PUSHORT)pRead->pLockedBuffer = READ_REGISTER_USHORT( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + pRead->Address ) );
			}
			else
			{
				READ_REGISTER_BUFFER_UCHAR( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + pRead->Address ), pRead->pLockedBuffer, pRead->BytesRead );
			}
//------------------------------------------------------------ MLZ 6/19/98 }
			Status = STATUS_SUCCESS;
			break;

 		case KME_FLAG_PCI_MEMORY_OTHER:

			if ( pExt->Hardware.BusType != PCIBus )
			{
				Status = STATUS_INVALID_PARAMETER_4;
				break;
			}

			if ( pRead->Address >= pExt->Hardware.Memory2.Length )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			pRead->BytesRead = pRead->Address + pRead->Length < pExt->Hardware.Memory2.Length ?
			                   pRead->Length : pExt->Hardware.Memory2.Length - pRead->Address;

//------------------------------------------------------------ MLZ 6/19/98 {
// TODO: Modify DxbAdapterRead to support other memory windows!

			if ( pRead->BytesRead == sizeof(ULONG) && IsDwordAligned( pRead->Address ) )
			{
				// It's a dword, write it as such
				*(PULONG)pRead->pLockedBuffer = READ_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.Memory2.Virtual + pRead->Address ) );
			}
			else if ( pRead->BytesRead == sizeof(USHORT) && IsWordAligned( pRead->Address ) )
			{
				// It's a word, write it as such
				*(PUSHORT)pRead->pLockedBuffer = READ_REGISTER_USHORT( (PVOID)( (PCHAR)pExt->Hardware.Memory2.Virtual + pRead->Address ) );
			}
			else
			{
				READ_REGISTER_BUFFER_UCHAR( (PVOID)( (PCHAR)pExt->Hardware.Memory2.Virtual + pRead->Address ), pRead->pLockedBuffer, pRead->BytesRead );
			}
//------------------------------------------------------------ MLZ 6/19/98 }
			READ_REGISTER_BUFFER_UCHAR( (PVOID)( (PCHAR)pExt->Hardware.Memory2.Virtual + pRead->Address ), pRead->pLockedBuffer, pRead->BytesRead );
			Status = STATUS_SUCCESS;
			break;

		default:
			Status = STATUS_INVALID_PARAMETER_4;
			break;
	}

Exit:

//	DigiDump( DIGIKME, ( "Dxb!KmeRead: Address 0x%08x, Length 0x%x, Flags 0x%x\n", pRead->Address, pRead->Length, pRead->Flags ) );
//	DigiDump( DIGIKME, ( "Dxb!KmeRead: Bytes read:\n" ) );
//	DigiDumpData( DIGIKME, pRead->pLockedBuffer, pRead->Length );

	dxbIoCompleteRequest( pIrp, Status, NT_SUCCESS( Status ) ? sizeof *pRead : 0 );
	return( Status );
}


NTSTATUS
DxbKmeWrite(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp )
/*++

Description:

	This routine processes a KME write request.

Arguments:

	pExt
		The device extension identifies the adapter

	pIrp
		The IRP containing the request

Return Value:

   STATUS_SUCCESS or fail status
   
--*/
{
	const PKME_WRITE pWrite = pIrp->AssociatedIrp.SystemBuffer;
	NTSTATUS Status;

	// Don't do zero-length writes!
	if ( pWrite->Length == 0 )
	{
		Status = STATUS_INVALID_PARAMETER;
		goto Exit;
	}

	Status = DigiLockBuffer( pIrp, pWrite->pBuffer, pWrite->Length, &pWrite->pLockedBuffer );
	if ( NT_ERROR( Status ) )
		goto Exit;

//	DigiDump( DIGIKME, ( "Dxb!KmeWrite: Address 0x%08x, Length 0x%x, Flags 0x%x\n", pWrite->Address, pWrite->Length, pWrite->Flags ) );
//	DigiDump( DIGIKME, ( "Dxb!KmeWrite: Bytes written:\n" ) );
//	DigiDumpData( DIGIKME, pWrite->pLockedBuffer, pWrite->Length );

	switch ( pWrite->Flags )
	{
		case KME_FLAG_ADAPTER_MEMORY:

			if ( pWrite->Address >= pExt->Hardware.Memory.Length )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			pWrite->BytesWritten = pWrite->Address + pWrite->Length < pExt->Hardware.Memory.Length ?
			                       pWrite->Length : pExt->Hardware.Memory.Length - pWrite->Address;

			DxbAdapterWrite( pExt, pWrite->pLockedBuffer, pWrite->Address, pWrite->BytesWritten );
			Status = STATUS_SUCCESS;
			break;

		case KME_FLAG_HOST_MEMORY:

			// Write to kernel memory
			// Cheap hack for validating the address... kernel drivers should all be 
			// above the 2Gig virtual address.  
			if ( pWrite->Address < 0x80000000 )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			RtlCopyMemory( (PVOID)pWrite->Address, pWrite->pLockedBuffer, pWrite->Length );
		   pWrite->BytesWritten = pWrite->Length;
			Status = STATUS_SUCCESS;
			break;

		case KME_FLAG_PCI_INTERFACE_CHIP:

			if ( pExt->Hardware.BusType != PCIBus )
			{
				Status = STATUS_INVALID_PARAMETER_4;
				break;
			}

			if ( pWrite->Address >= pExt->Hardware.PciInterfaceChip.Length )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			pWrite->BytesWritten = pWrite->Address + pWrite->Length < pExt->Hardware.PciInterfaceChip.Length ?
			                       pWrite->Length : pExt->Hardware.PciInterfaceChip.Length - pWrite->Address;

//------------------------------------------------------------ MLZ 6/19/98 {
// TODO: Modify DxbAdapterWrite to support other memory windows!

			if ( pWrite->BytesWritten == sizeof(ULONG) && IsDwordAligned( pWrite->Address ) )
			{
				// It's a dword, write it as such
				WRITE_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + pWrite->Address ), *(PULONG)pWrite->pLockedBuffer );
			}
			else if ( pWrite->BytesWritten == sizeof(USHORT) && IsWordAligned( pWrite->Address ) )
			{
				// It's a word, write it as such
				WRITE_REGISTER_USHORT( (PVOID)( (PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + pWrite->Address ), *(PUSHORT)pWrite->pLockedBuffer );
			}
			else
			{
				WRITE_REGISTER_BUFFER_UCHAR( (PVOID)((PCHAR)pExt->Hardware.PciInterfaceChip.Virtual + pWrite->Address), pWrite->pLockedBuffer, pWrite->BytesWritten );
			}
//------------------------------------------------------------ MLZ 6/19/98 }

			Status = STATUS_SUCCESS;
			break;

		case KME_FLAG_PCI_MEMORY_OTHER:

			if ( pExt->Hardware.BusType != PCIBus )
			{
				Status = STATUS_INVALID_PARAMETER_4;
				break;
			}

			if ( pWrite->Address >= pExt->Hardware.Memory2.Length )
			{
				Status = STATUS_INVALID_PARAMETER;
				break;
			}

			pWrite->BytesWritten = pWrite->Address + pWrite->Length < pExt->Hardware.Memory2.Length ?
			                       pWrite->Length : pExt->Hardware.Memory2.Length - pWrite->Address;

//------------------------------------------------------------ MLZ 6/19/98 {
// TODO: Modify DxbAdapterWrite to support other memory windows!

			if ( pWrite->BytesWritten == sizeof(ULONG) && IsDwordAligned( pWrite->Address ) )
			{
				// It's a dword, write it as such
				WRITE_REGISTER_ULONG( (PVOID)( (PCHAR)pExt->Hardware.Memory2.Virtual + pWrite->Address ), *(PULONG)pWrite->pLockedBuffer );
			}
			else if ( pWrite->BytesWritten == sizeof(USHORT) && IsWordAligned( pWrite->Address ) )
			{
				// It's a word, write it as such
				WRITE_REGISTER_USHORT( (PVOID)( (PCHAR)pExt->Hardware.Memory2.Virtual + pWrite->Address ), *(PUSHORT)pWrite->pLockedBuffer );
			}
			else
			{
				WRITE_REGISTER_BUFFER_UCHAR( (PVOID)((PCHAR)pExt->Hardware.Memory2.Virtual + pWrite->Address), pWrite->pLockedBuffer, pWrite->BytesWritten );
			}
//------------------------------------------------------------ MLZ 6/19/98 }
			Status = STATUS_SUCCESS;
			break;

		default:
			Status = STATUS_INVALID_PARAMETER_4;
			break;
	}

Exit:

	dxbIoCompleteRequest( pIrp, Status, NT_SUCCESS( Status ) ? sizeof *pWrite : 0 );
	return( Status );
}


STATIC NTSTATUS
DxbKmeOpenPhysicalMemoryHandle(
	OUT PHANDLE pPhysicalMemoryHandle )
/*++

Description:

	This routine opens a handle to \\Device\\PhysicalMemory

	It is left to the caller of this function to use ZwClose()
	to close this handle when they are done using it.

Arguments:

	pPhysicalMemoryHandle
		The pointer to the handle for \\Device\\PhysicalMemory

Return Value:

   STATUS_SUCCESS or fail status
   
--*/
{
	NTSTATUS Status;
	HANDLE PhysicalMemoryHandle = NULL;

	ASSERT( pPhysicalMemoryHandle != NULL );

	// DigiDump( DIGIKME, ( "Dxb!OpenPhysicalMemoryHandle:\n" ) );

	//
	// Get a pointer to physical memory...
	//
	// - Create the name
	// - Initialize the data to find the object
	// - Open a handle to the oject and check the status
	// - Get a pointer to the object
	// - Free the handle
	//

	{
		UNICODE_STRING PhysicalMemoryUnicodeString;
		OBJECT_ATTRIBUTES ObjAttributes;

		RtlInitUnicodeString( &PhysicalMemoryUnicodeString, L"\\Device\\PhysicalMemory" );
		InitializeObjectAttributes( &ObjAttributes,
			&PhysicalMemoryUnicodeString,
			OBJ_CASE_INSENSITIVE,
			(HANDLE) NULL,
			(PSECURITY_DESCRIPTOR) NULL );


		// DigiDump( DIGIKME, ( "Dxb!OpenPhysicalMemoryHandle: Calling ZwOpenSection()\n" ) );
		Status = ZwOpenSection( &PhysicalMemoryHandle,
						SECTION_ALL_ACCESS,
						&ObjAttributes );

		if( !NT_SUCCESS( Status ) )
		{
			DigiDump( DIGIERROR, ( "Dxb!OpenPhysicalMemoryHandle: ZwOpenSection() failed.  Status == %d\n", Status ) );
			goto Exit;
		}
	}

	{
		PVOID PhysicalMemorySection = NULL;

		// DigiDump( DIGIKME, ( "Dxb!ObReferenceObjectByHandle: Calling ZwOpenSection()\n" ) );
		Status = ObReferenceObjectByHandle( PhysicalMemoryHandle,
						SECTION_ALL_ACCESS,
						(POBJECT_TYPE) NULL,
						KernelMode,
						&PhysicalMemorySection,
						(POBJECT_HANDLE_INFORMATION) NULL );

		if( !NT_SUCCESS( Status ) )
		{
			DigiDump( DIGIERROR, ( "Dxb!OpenPhysicalMemoryHandle: ObReferenceObjectByHandle() failed.  Status == %d\n", Status ) );
			goto Exit;
		}
	}

	*pPhysicalMemoryHandle = PhysicalMemoryHandle;
	Status = STATUS_SUCCESS;

Exit:
	return Status;
}


NTSTATUS
DxbKmeMap(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp )
/*++

Description:

	This routine maps in a view of memory or IO of the associated
	adapter to a virtual address to be used by the calling process.

	If successful, the calling process should use DxbDmeUnmap() to
	free any resources allocated during the map.

Arguments:

	pExt
		The device extension identifies the adapter

	pIrp
		The IRP containing the request

Return Value:

	STATUS_SUCCESS or fail status

--*/
{
	PKME_MAP		pMap = pIrp->AssociatedIrp.SystemBuffer;
	HANDLE		PhysicalMemoryHandle = NULL;
	NTSTATUS		Status;

	ASSERT( pMap != NULL );

	DigiDump( DIGIKME, ( "Dxb!KmeMap: %S\n", pExt->Name.Buffer ) );

	Status = DxbKmeOpenPhysicalMemoryHandle( &PhysicalMemoryHandle );
	if( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERROR, ( "Dxb!KmeMap: DxbKmeOpenPhysicalMemoryHandle() failed.  Status == %d\n", Status ) );
		goto Exit;
	}

	switch( pMap->Flags )
	{
		case KME_FLAG_ADAPTER_MEMORY:
		{
			PVOID					pv = NULL;
			ULONG					cbToMap = pExt->Hardware.Memory.cbWindow;
			LARGE_INTEGER		ViewBase = pExt->Hardware.Memory.Physical;
			ULONG_PTR			cbMapped = pExt->Hardware.Memory.cbWindow;

			// DigiDump( DIGIKME, ( "Dxb!KmeMap: Calling ZwMapViewOfSection()\n" ) );
			Status = ZwMapViewOfSection( PhysicalMemoryHandle,
							(HANDLE) -1,	// means current process
							&pv,
							0,
							cbToMap,
							&ViewBase,
							&cbMapped,
							ViewShare,
							0,
							PAGE_READWRITE | PAGE_NOCACHE );
			if( !NT_SUCCESS( Status ) )
			{
				DigiDump( DIGIERROR, ( "Dxb!KmeMap: ZwMapViewOfSection() failed.  Status == %d\n", Status ) );
				goto Exit;
			}

			//
			// Mapping the section above rounded the physical address down to the
			// nearest 64 K boundary. Now return a virtual address that sits where
			// we want by adding in the offset from the beginning of the section.
			//
			ASSERT( cbMapped >= pExt->Hardware.Memory.cbWindow );
			pMap->pView = (PVOID)( (ULONG_PTR)pv + (ULONG_PTR)pExt->Hardware.Memory.Physical.LowPart - (ULONG_PTR)ViewBase.LowPart );
			pMap->Length = pExt->Hardware.Memory.cbWindow;

			Status = STATUS_SUCCESS;

			break;
		}
		
		default:
			Status = STATUS_INVALID_PARAMETER_4;
			break;
	}

Exit:
	if ( PhysicalMemoryHandle != NULL )
	{
		ZwClose( PhysicalMemoryHandle );
	}

	dxbIoCompleteRequest( pIrp, Status, NT_SUCCESS( Status ) ? sizeof *pMap : 0 );
	return Status;
}


NTSTATUS
DxbKmeUnmap(
	PIRP pIrp )
/*++

Description:

	This routine unmaps a section view originally mapped by DxbKmeMap( ).

Arguments:

	pIrp
		The IRP containing the request

Return Value:

	STATUS_SUCCESS or fail status

--*/
{
	PVOID pv = *((PVOID *)pIrp->AssociatedIrp.SystemBuffer);
	NTSTATUS Status;

	DigiDump( DIGIKME, ( "Dxb!KmeUnmap: 0x%08x\n", pv) );

	Status = ZwUnmapViewOfSection( (HANDLE)-1, pv );
	if( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERROR, ( "Dxb!KmeMap: ZwUnmapViewOfSection() failed.  Status == %d\n", Status ) );
	}

	dxbIoCompleteRequest( pIrp, Status, 0 );
	return Status;
}
