Windows2003-3790/drivers/storage/scsiport/verify.c
2020-09-30 16:53:55 +02:00

3109 lines
92 KiB
C

/*++
Copyright (C) Microsoft Corporation, 1990 - 2000
Module Name:
verify.c
Abstract:
This module implements a driver verifier extension for the SCSI port
driver. Scsiport adds some of its exports to the list of apis that get
thunked by the system driver verifier, thus enabling scsiport-specific
verification of miniport drivers.
Authors:
John Strange (JohnStra)
Environment:
kernel mode only
Revision History:
--*/
#include "port.h"
#if DBG
static const char *__file__ = __FILE__;
#endif
#define __FILE_ID__ 'vfry'
SCSIPORT_API
ULONG
ScsiPortInitializeVrfy(
IN PVOID Argument1,
IN PVOID Argument2,
IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
IN PVOID HwContext
);
SCSIPORT_API
VOID
ScsiPortCompleteRequestVrfy(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN UCHAR SrbStatus
);
SCSIPORT_API
PSCSI_REQUEST_BLOCK
ScsiPortGetSrbVrfy(
IN PVOID DeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN LONG QueueTag
);
SCSIPORT_API
PVOID
ScsiPortGetDeviceBaseVrfy(
IN PVOID HwDeviceExtension,
IN INTERFACE_TYPE BusType,
IN ULONG SystemIoBusNumber,
IN SCSI_PHYSICAL_ADDRESS IoAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN InIoSpace
);
SCSIPORT_API
VOID
ScsiPortNotificationVrfy(
IN SCSI_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
...
);
SCSIPORT_API
VOID
ScsiPortFlushDmaVrfy(
IN PVOID DeviceExtension
);
SCSIPORT_API
VOID
ScsiPortFreeDeviceBaseVrfy(
IN PVOID HwDeviceExtension,
IN PVOID MappedAddress
);
SCSIPORT_API
ULONG
ScsiPortGetBusDataVrfy(
IN PVOID DeviceExtension,
IN ULONG BusDataType,
IN ULONG SystemIoBusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Length
);
SCSIPORT_API
PVOID
ScsiPortGetLogicalUnitVrfy(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun
);
SCSIPORT_API
SCSI_PHYSICAL_ADDRESS
ScsiPortGetPhysicalAddressVrfy(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN PVOID VirtualAddress,
OUT ULONG *Length
);
SCSIPORT_API
PVOID
ScsiPortGetUncachedExtensionVrfy(
IN PVOID HwDeviceExtension,
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN ULONG NumberOfBytes
);
SCSIPORT_API
PVOID
ScsiPortGetVirtualAddressVrfy(
IN PVOID HwDeviceExtension,
IN SCSI_PHYSICAL_ADDRESS PhysicalAddress
);
SCSIPORT_API
VOID
ScsiPortIoMapTransferVrfy(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN PVOID LogicalAddress,
IN ULONG Length
);
SCSIPORT_API
VOID
ScsiPortMoveMemoryVrfy(
IN PVOID WriteBuffer,
IN PVOID ReadBuffer,
IN ULONG Length
);
SCSIPORT_API
ULONG
ScsiPortSetBusDataByOffsetVrfy(
IN PVOID DeviceExtension,
IN ULONG BusDataType,
IN ULONG SystemIoBusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Offset,
IN ULONG Length
);
SCSIPORT_API
BOOLEAN
ScsiPortValidateRangeVrfy(
IN PVOID HwDeviceExtension,
IN INTERFACE_TYPE BusType,
IN ULONG SystemIoBusNumber,
IN SCSI_PHYSICAL_ADDRESS IoAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN InIoSpace
);
SCSIPORT_API
VOID
ScsiPortStallExecutionVrfy(
IN ULONG Delay
);
PVOID
SpAllocateContiguousChunk(
IN PDRIVER_OBJECT DriverObject,
IN PDMA_ADAPTER DmaAdapterObject,
IN BOOLEAN Dma64BitAddresses,
IN ULONG Length,
IN ULONG Align,
OUT PHYSICAL_ADDRESS *PhysicalCommonBuffer,
OUT BOOLEAN *CommonBuffer
);
PVOID
SpRemapBlock(
IN PVOID BlockVa,
IN ULONG BlockSize,
OUT PMDL* Mdl
);
BOOLEAN
SpCheckForActiveRequests(
IN PADAPTER_EXTENSION
);
ULONG
SpGetAdapterVerifyLevel(
IN PADAPTER_EXTENSION Adapter
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, SpVerifierInitialization)
#pragma alloc_text(PAGE, ScsiPortInitializeVrfy)
#pragma alloc_text(PAGE, SpGetAdapterVerifyLevel)
#pragma alloc_text(PAGE, SpDoVerifierInit)
#pragma alloc_text(PAGEVRFY1, ScsiPortGetSrbVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortCompleteRequestVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortGetDeviceBaseVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortNotificationVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortFlushDmaVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortFreeDeviceBaseVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortGetBusDataVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortGetLogicalUnitVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortGetPhysicalAddressVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortGetUncachedExtensionVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortGetVirtualAddressVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortMoveMemoryVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortSetBusDataByOffsetVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortValidateRangeVrfy)
#pragma alloc_text(PAGEVRFY1, ScsiPortStallExecutionVrfy)
#pragma alloc_text(PAGEVRFY, SpHwFindAdapterVrfy)
#pragma alloc_text(PAGEVRFY, SpHwInitializeVrfy)
#pragma alloc_text(PAGEVRFY, SpHwStartIoVrfy)
#pragma alloc_text(PAGEVRFY, SpHwInterruptVrfy)
#pragma alloc_text(PAGEVRFY, SpHwResetBusVrfy)
#pragma alloc_text(PAGEVRFY, SpHwDmaStartedVrfy)
#pragma alloc_text(PAGEVRFY, SpHwRequestInterruptVrfy)
#pragma alloc_text(PAGEVRFY, SpHwTimerRequestVrfy)
#pragma alloc_text(PAGEVRFY, SpHwAdapterControlVrfy)
#pragma alloc_text(PAGEVRFY, SpVerifySrbStatus)
#pragma alloc_text(PAGEVRFY, SpAllocateContiguousChunk)
#pragma alloc_text(PAGEVRFY, SpGetCommonBufferVrfy)
#pragma alloc_text(PAGEVRFY, SpFreeCommonBufferVrfy)
#pragma alloc_text(PAGEVRFY, SpGetOriginalSrbExtVa)
#pragma alloc_text(PAGEVRFY, SpInsertSrbExtension)
#pragma alloc_text(PAGEVRFY, SpPrepareSrbExtensionForUse)
#pragma alloc_text(PAGEVRFY, SpPrepareSenseBufferForUse)
#pragma alloc_text(PAGEVRFY, SpRemapBlock)
#pragma alloc_text(PAGEVRFY, SpGetInaccessiblePage)
#pragma alloc_text(PAGEVRFY, SpEnsureAllRequestsAreComplete)
#pragma alloc_text(PAGEVRFY, SpCheckForActiveRequests)
#endif
//
// Some defines and a macro for conditionally verifying based on the
// verification level.
//
#define SP_DONT_CHK_HW_INITIALIZE_DURATION 0x80000000
#define SP_DONT_CHK_ACTIVE_UNTAGGED_REQUEST 0x40000000
#define SP_DONT_CHK_REQUESTS_ON_RESET 0x20000000
#define SP_DONT_CHK_HW_ADAPTERCONTROL_DURATION 0x10000000
#define VRFY_DO_CHECK(adapter, chk)\
(((adapter)->VerifierExtension != NULL) &&\
(((adapter)->VerifierExtension->VrfyLevel & (chk)) == 0))
//
// Indicates whether scsiport's verifier functionality has been initialized.
//
ULONG ScsiPortVerifierInitialized = 0;
//
// Handle to pageable verifier code sections. We manually lock the verify
// code into memory iff we need it.
//
PVOID VerifierCodeSectionHandle = NULL;
PVOID VerifierApiCodeSectionHandle = NULL;
//
// Time increment of the interval timer in 100 ns units. We use this to
// calculate the time miniport routines execute so we can catch those that
// run longer than they should.
//
ULONG TimeIncrement;
//
// Global variable used to control verification aggressiveness. This value
// is used in conjuction with a per-adapter registry value, to control what
// type of verification we do on a particular miniport.
//
ULONG SpVrfyLevel = 0;
//
// Global variable used to control how aggressively we seek out stall
// offenders. Default is a tenth of a second.
//
ULONG SpVrfyMaximumStall = 100000;
//
// When verifier needs a unique address, this is what it uses.
//
ULONG SpMarker = 0x59465256;
//
// This table represents the functions verify will thunk for us.
//
#define SPVERIFIERFUNC(pfn) ((PDRIVER_VERIFIER_THUNK_ROUTINE)(pfn))
const DRIVER_VERIFIER_THUNK_PAIRS ScsiPortVerifierFunctionTable[] =
{
{SPVERIFIERFUNC(ScsiPortInitialize), SPVERIFIERFUNC(ScsiPortInitializeVrfy)},
{SPVERIFIERFUNC(ScsiPortGetSrb), SPVERIFIERFUNC(ScsiPortGetSrbVrfy)},
{SPVERIFIERFUNC(ScsiPortCompleteRequest), SPVERIFIERFUNC(ScsiPortCompleteRequestVrfy)},
{SPVERIFIERFUNC(ScsiPortGetDeviceBase), SPVERIFIERFUNC(ScsiPortGetDeviceBaseVrfy)},
{SPVERIFIERFUNC(ScsiPortNotification), SPVERIFIERFUNC(ScsiPortNotificationVrfy)},
{SPVERIFIERFUNC(ScsiPortFlushDma), SPVERIFIERFUNC(ScsiPortFlushDmaVrfy)},
{SPVERIFIERFUNC(ScsiPortFreeDeviceBase), SPVERIFIERFUNC(ScsiPortFreeDeviceBaseVrfy)},
{SPVERIFIERFUNC(ScsiPortGetBusData), SPVERIFIERFUNC(ScsiPortGetBusDataVrfy)},
{SPVERIFIERFUNC(ScsiPortGetLogicalUnit), SPVERIFIERFUNC(ScsiPortGetLogicalUnitVrfy)},
{SPVERIFIERFUNC(ScsiPortGetPhysicalAddress), SPVERIFIERFUNC(ScsiPortGetPhysicalAddressVrfy)},
{SPVERIFIERFUNC(ScsiPortGetUncachedExtension), SPVERIFIERFUNC(ScsiPortGetUncachedExtensionVrfy)},
{SPVERIFIERFUNC(ScsiPortGetVirtualAddress), SPVERIFIERFUNC(ScsiPortGetVirtualAddressVrfy)},
{SPVERIFIERFUNC(ScsiPortIoMapTransfer), SPVERIFIERFUNC(ScsiPortIoMapTransferVrfy)},
{SPVERIFIERFUNC(ScsiPortMoveMemory), SPVERIFIERFUNC(ScsiPortMoveMemoryVrfy)},
{SPVERIFIERFUNC(ScsiPortSetBusDataByOffset), SPVERIFIERFUNC(ScsiPortSetBusDataByOffsetVrfy)},
{SPVERIFIERFUNC(ScsiPortValidateRange), SPVERIFIERFUNC(ScsiPortValidateRangeVrfy)},
{SPVERIFIERFUNC(ScsiPortStallExecution), SPVERIFIERFUNC(ScsiPortStallExecutionVrfy)},
};
BOOLEAN
SpVerifierInitialization(
VOID
)
/*++
Routine Description:
This routine initializes scsiport's verifier functionality.
Adds several of scsiport's exported functions to the list of routines
thunked by the system verifier.
Arguments:
VOID
Return Value:
TRUE if verifier is successfully initialized.
--*/
{
ULONG Flags;
NTSTATUS Status;
PAGED_CODE();
//
// Query the system for verifier information. This is to ensure that
// verifier is present and operational on the system. If this fails, we
// give up and return FALSE.
//
Status = MmIsVerifierEnabled (&Flags);
if (NT_SUCCESS(Status)) {
//
// Add scsiport APIs to the set that will be thunked by the system
// for verification.
//
Status = MmAddVerifierThunks ((VOID *) ScsiPortVerifierFunctionTable,
sizeof(ScsiPortVerifierFunctionTable));
if (NT_SUCCESS(Status)) {
//
// Set the system query time increment. Our verifier code uses
// this to calculate the time miniport routines take to execute.
//
TimeIncrement = KeQueryTimeIncrement();
return TRUE;
}
}
return FALSE;
}
ULONG
ScsiPortInitializeVrfy(
IN PVOID Argument1,
IN PVOID Argument2,
IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
IN PVOID HwContext
)
{
ULONG Result;
PDRIVER_OBJECT DriverObject = Argument1;
PSCSIPORT_DRIVER_EXTENSION DriverExtension;
PAGED_CODE();
//
// Lock the thunked API routines down.
//
#ifdef ALLOC_PRAGMA
if (VerifierApiCodeSectionHandle == NULL) {
VerifierApiCodeSectionHandle = MmLockPagableCodeSection(ScsiPortGetSrbVrfy);
}
#endif
if (Argument1 == NULL || Argument2 == NULL) {
//
// Argument1 and Argument2 must be non-NULL.
//
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_BAD_INIT_PARAMS,
(ULONG_PTR)Argument1,
(ULONG_PTR)Argument2,
0);
}
//
// Forward the call on to ScsiPortInitialize.
//
Result = ScsiPortInitialize (Argument1,
Argument2,
HwInitializationData,
HwContext);
//
// If initialization was successful, try to initialize verifier settings.
//
if (NT_SUCCESS(Result)) {
DriverExtension = IoGetDriverObjectExtension (DriverObject,
ScsiPortInitialize);
if (DriverExtension != NULL) {
//
// Indicate that the driver is being verified by scsiport.
//
InterlockedExchange(&DriverExtension->Verifying, 1);
}
}
return Result;
}
PSCSI_REQUEST_BLOCK
ScsiPortGetSrbVrfy(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN LONG QueueTag
)
{
return ScsiPortGetSrb(HwDeviceExtension,
PathId,
TargetId,
Lun,
QueueTag);
}
VOID
ScsiPortCompleteRequestVrfy(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN UCHAR SrbStatus
)
{
ScsiPortCompleteRequest(HwDeviceExtension,
PathId,
TargetId,
Lun,
SrbStatus);
}
PVOID
ScsiPortGetDeviceBaseVrfy(
IN PVOID HwDeviceExtension,
IN INTERFACE_TYPE BusType,
IN ULONG SystemIoBusNumber,
SCSI_PHYSICAL_ADDRESS IoAddress,
ULONG NumberOfBytes,
BOOLEAN InIoSpace
)
{
return ScsiPortGetDeviceBase(HwDeviceExtension,
BusType,
SystemIoBusNumber,
IoAddress,
NumberOfBytes,
InIoSpace);
}
VOID
ScsiPortNotificationVrfy(
IN SCSI_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
...
)
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
PLOGICAL_UNIT_EXTENSION logicalUnit;
PSCSI_REQUEST_BLOCK Srb;
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
PHW_INTERRUPT HwRequestInterrupt;
PHW_INTERRUPT HwTimerRequest;
ULONG MiniportTimerValue;
PWNODE_EVENT_ITEM WnodeEventItem;
PSRB_DATA srbData;
va_list ap;
va_start(ap, HwDeviceExtension);
switch (NotificationType) {
case NextRequest:
ScsiPortNotification(NotificationType, HwDeviceExtension);
va_end(ap);
return;
case RequestComplete:
Srb = va_arg(ap, PSCSI_REQUEST_BLOCK);
//
// Check that the status makes sense.
//
SpVerifySrbStatus(HwDeviceExtension, Srb);
//
// Check that this request has not already been completed.
//
if ((Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE) == 0) {
KeBugCheckEx(
SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_REQUEST_COMPLETED_TWICE,
(ULONG_PTR)HwDeviceExtension,
(ULONG_PTR)Srb,
0);
}
//
// Restore the DataBuffer in the SRB if we plugged in our
// pointer to unmapped memory. We did this if the adapter
// does not specify MappedBuffers because the miniport is
// not supposed to touch the buffer in that case.
//
srbData = (PSRB_DATA)Srb->OriginalRequest;
ASSERT_SRB_DATA(srbData);
if (srbData->UnmappedDataBuffer != &SpMarker) {
ASSERT(srbData->UnmappedDataBuffer != NULL);
Srb->DataBuffer = srbData->UnmappedDataBuffer;
}
srbData->UnmappedDataBuffer = NULL;
//
// Forward on to the real ScsiPortNotification routine.
//
ScsiPortNotification(NotificationType,
HwDeviceExtension,
Srb);
va_end(ap);
return;
case ResetDetected:
ScsiPortNotification(NotificationType,
HwDeviceExtension);
va_end(ap);
return;
case NextLuRequest:
//
// The miniport driver is ready for the next request and
// can accept a request for this logical unit.
//
PathId = va_arg(ap, UCHAR);
TargetId = va_arg(ap, UCHAR);
Lun = va_arg(ap, UCHAR);
logicalUnit = deviceExtension->CachedLogicalUnit;
if ((logicalUnit == NULL)
|| (logicalUnit->TargetId != TargetId)
|| (logicalUnit->PathId != PathId)
|| (logicalUnit->Lun != Lun)) {
logicalUnit = GetLogicalUnitExtension(deviceExtension,
PathId,
TargetId,
Lun,
FALSE,
FALSE);
}
//
// Bugcheck if there is an untagged request active for this
// logical unit.
//
if (VRFY_DO_CHECK(deviceExtension, SP_DONT_CHK_ACTIVE_UNTAGGED_REQUEST) &&
logicalUnit != NULL &&
logicalUnit->CurrentUntaggedRequest != NULL &&
logicalUnit->CurrentUntaggedRequest->CurrentSrb != NULL &&
logicalUnit->CurrentUntaggedRequest->CurrentSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
KeBugCheckEx (
SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_UNTAGGED_REQUEST_ACTIVE,
(ULONG_PTR)HwDeviceExtension,
(ULONG_PTR)logicalUnit,
0);
}
//
// Forward on to the real ScsiPortNotification.
//
ScsiPortNotification(NotificationType,
HwDeviceExtension,
PathId,
TargetId,
Lun);
va_end(ap);
return;
case CallDisableInterrupts:
HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
ScsiPortNotification(NotificationType,
HwDeviceExtension,
HwRequestInterrupt);
va_end(ap);
return;
case CallEnableInterrupts:
HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
ScsiPortNotification(NotificationType,
HwDeviceExtension,
HwRequestInterrupt);
va_end(ap);
return;
case RequestTimerCall:
HwTimerRequest = va_arg(ap, PHW_INTERRUPT);
MiniportTimerValue = va_arg(ap, ULONG);
ScsiPortNotification(NotificationType,
HwDeviceExtension,
HwTimerRequest,
MiniportTimerValue);
va_end(ap);
return;
case WMIEvent:
WnodeEventItem = va_arg(ap, PWNODE_EVENT_ITEM);
PathId = va_arg(ap, UCHAR);
if (PathId != 0xFF) {
TargetId = va_arg(ap, UCHAR);
Lun = va_arg(ap, UCHAR);
ScsiPortNotification(NotificationType,
HwDeviceExtension,
WnodeEventItem,
PathId,
TargetId,
Lun);
} else {
ScsiPortNotification(NotificationType,
HwDeviceExtension,
WnodeEventItem,
PathId);
}
va_end(ap);
return;
case WMIReregister:
PathId = va_arg(ap, UCHAR);
if (PathId != 0xFF) {
TargetId = va_arg(ap, UCHAR);
Lun = va_arg(ap, UCHAR);
ScsiPortNotification(NotificationType,
HwDeviceExtension,
PathId,
TargetId,
Lun);
} else {
ScsiPortNotification(NotificationType,
HwDeviceExtension,
PathId);
}
va_end(ap);
return;
case BusChangeDetected:
ScsiPortNotification(NotificationType,
HwDeviceExtension);
va_end(ap);
return;
default:
ScsiPortNotification(NotificationType,
HwDeviceExtension);
va_end(ap);
return;
}
}
VOID
ScsiPortFlushDmaVrfy(
IN PVOID DeviceExtension
)
{
ScsiPortFlushDma(DeviceExtension);
return;
}
VOID
ScsiPortFreeDeviceBaseVrfy(
IN PVOID HwDeviceExtension,
IN PVOID MappedAddress
)
{
ScsiPortFreeDeviceBase(HwDeviceExtension, MappedAddress);
return;
}
ULONG
ScsiPortGetBusDataVrfy(
IN PVOID DeviceExtension,
IN ULONG BusDataType,
IN ULONG SystemIoBusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Length
)
{
ULONG BusData;
if (BusDataType != PCIConfiguration) {
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_BAD_BUSDATATYPE,
(ULONG_PTR)BusDataType,
(ULONG_PTR)DeviceExtension,
(ULONG_PTR)SystemIoBusNumber);
}
BusData = ScsiPortGetBusData(DeviceExtension,
BusDataType,
SystemIoBusNumber,
SlotNumber,
Buffer,
Length);
return BusData;
}
PVOID
ScsiPortGetLogicalUnitVrfy(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun
)
{
PVOID LogicalUnit;
LogicalUnit = ScsiPortGetLogicalUnit(HwDeviceExtension,
PathId,
TargetId,
Lun);
return LogicalUnit;
}
SCSI_PHYSICAL_ADDRESS
ScsiPortGetPhysicalAddressVrfy(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN PVOID VirtualAddress,
OUT ULONG *Length
)
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
PHYSICAL_ADDRESS address;
PSP_VA_MAPPING_INFO MappingInfo;
ULONG byteOffset;
ULONG length;
if ((deviceExtension->VerifierExtension != NULL) &&
(deviceExtension->VerifierExtension->VrfyLevel & SP_VRFY_COMMON_BUFFERS) &&
(Srb == NULL || Srb->SenseInfoBuffer == VirtualAddress)) {
ULONG i;
PVOID* BlkAddr;
PUCHAR Beginning, End;
PHYSICAL_ADDRESS *AddressBlock;
//
// Initialize a pointer to our array of common memory block
// descriptors. We use this to locate the block that contains
// the VA.
//
BlkAddr = deviceExtension->VerifierExtension->CommonBufferVAs;
//
// Look for the block that contains the VA.
//
for (i = 0; i < deviceExtension->NumberOfRequests; i++) {
//
// First, check if the VA is in the SRB extension.
//
MappingInfo = GET_VA_MAPPING_INFO(deviceExtension, BlkAddr[i]);
if (MappingInfo->RemappedSrbExtVa != NULL) {
Beginning = MappingInfo->RemappedSrbExtVa;
} else {
Beginning = BlkAddr[i];
}
End = (PUCHAR)ROUND_TO_PAGES((PUCHAR)Beginning + deviceExtension->SrbExtensionSize);
if ((PUCHAR)VirtualAddress >= Beginning &&
(PUCHAR)VirtualAddress < End) {
byteOffset = (ULONG)((PUCHAR)VirtualAddress - Beginning);
break;
}
//
// Next, check if the VA is in the Sense Data Buffer.
//
if (deviceExtension->AutoRequestSense == TRUE) {
if (MappingInfo->RemappedSenseVa != NULL) {
Beginning = MappingInfo->RemappedSenseVa;
} else {
Beginning = (PUCHAR)BlkAddr[i] +
ROUND_TO_PAGES(deviceExtension->SrbExtensionSize);
}
End = Beginning + PAGE_SIZE;
if ((PUCHAR)VirtualAddress >= Beginning &&
(PUCHAR)VirtualAddress < End) {
byteOffset = (ULONG)((PUCHAR)VirtualAddress - Beginning) +
(ULONG)ROUND_TO_PAGES(deviceExtension->SrbExtensionSize);
break;
}
}
}
//
// If we haven't found the VA yet, it must be in the non-cached
// extension.
//
if (i == deviceExtension->NumberOfRequests) {
if (deviceExtension->VerifierExtension->NonCachedBufferSize != 0) {
Beginning = BlkAddr[i];
End = (PUCHAR) ROUND_TO_PAGES(
(PUCHAR)Beginning +
deviceExtension->VerifierExtension->NonCachedBufferSize);
if ((PUCHAR)VirtualAddress < Beginning &&
(PUCHAR)VirtualAddress >= End) {
KeBugCheckEx (SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_BAD_VA,
(ULONG_PTR)HwDeviceExtension,
(ULONG_PTR)VirtualAddress,
0);
}
byteOffset = (ULONG)((PUCHAR)VirtualAddress - Beginning);
} else {
KeBugCheckEx (SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_BAD_VA,
(ULONG_PTR)HwDeviceExtension,
(ULONG_PTR)VirtualAddress,
0);
}
}
//
// Get the physical address.
//
AddressBlock = deviceExtension->VerifierExtension->CommonBufferPAs;
address.QuadPart = AddressBlock[i].QuadPart + byteOffset;
//
// Calculate the length of the block.
//
length = (ULONG)((End - (PUCHAR)VirtualAddress) + 1);
return address;
}
//
// Forward on to the real routine.
//
address = ScsiPortGetPhysicalAddress(HwDeviceExtension,
Srb,
VirtualAddress,
Length);
return address;
}
PVOID
ScsiPortGetUncachedExtensionVrfy(
IN PVOID HwDeviceExtension,
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN ULONG NumberOfBytes
)
{
PVOID Extension;
Extension = ScsiPortGetUncachedExtension(HwDeviceExtension,
ConfigInfo,
NumberOfBytes);
return Extension;
}
PVOID
ScsiPortGetVirtualAddressVrfy(
IN PVOID HwDeviceExtension,
IN SCSI_PHYSICAL_ADDRESS PhysicalAddress
)
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
PVOID* BlkAddr;
PSP_VA_MAPPING_INFO MappingInfo;
ULONG smallphysicalBase;
ULONG smallAddress;
PVOID address;
ULONG offset;
ULONG Size;
ULONG i;
//
// If the adapter is not configured to allocate multiple common buffer
// blocks during verification, just call the scsiport routine.
//
if ((deviceExtension->VerifierExtension == NULL) ||
(deviceExtension->VerifierExtension->VrfyLevel & SP_VRFY_COMMON_BUFFERS) == 0) {
return ScsiPortGetVirtualAddress(HwDeviceExtension, PhysicalAddress);
}
BlkAddr = deviceExtension->VerifierExtension->CommonBufferVAs;
//
// Convert the 64-bit physical address to a ULONG.
//
smallAddress = ScsiPortConvertPhysicalAddressToUlong(PhysicalAddress);
//
// Check first if the supplied physical address is in an SRB extension or
// in a sense buffer.
//
for (i = 0; i < deviceExtension->NumberOfRequests; i++) {
smallphysicalBase =
ScsiPortConvertPhysicalAddressToUlong(
deviceExtension->VerifierExtension->CommonBufferPAs[i]);
if ((smallAddress < smallphysicalBase) ||
(smallAddress >= smallphysicalBase +
(deviceExtension->CommonBufferSize -
PAGE_SIZE))) {
continue;
}
//
// Calculate the address of the buffer.
//
offset = smallAddress - smallphysicalBase;
address = offset + (PUCHAR)BlkAddr[i];
MappingInfo = GET_VA_MAPPING_INFO(deviceExtension, BlkAddr[i]);
goto GotAddress;
}
//
// Check if the supplied physical address is in the non-cached extension.
//
if (deviceExtension->VerifierExtension->NonCachedBufferSize == 0) {
ASSERT(FALSE);
return(NULL);
} else {
smallphysicalBase =
ScsiPortConvertPhysicalAddressToUlong(
deviceExtension->VerifierExtension->CommonBufferPAs[i]);
if ((smallAddress < smallphysicalBase) ||
(smallAddress >= smallphysicalBase +
deviceExtension->VerifierExtension->NonCachedBufferSize)) {
//
// This is a bogus physical address return back NULL.
//
ASSERT(FALSE);
return(NULL);
}
offset = smallAddress - smallphysicalBase;
address = offset + (PUCHAR)BlkAddr[i];
Size = (ULONG)ROUND_TO_PAGES(deviceExtension->VerifierExtension->NonCachedBufferSize);
MappingInfo = (PSP_VA_MAPPING_INFO)((PUCHAR)BlkAddr[i] + (Size - PAGE_SIZE));
}
GotAddress:
//
// Find out if we've remapped this address. If we have, give the
// caller the second mapping.
//
if (address < MappingInfo->OriginalSenseVa &&
MappingInfo->RemappedSrbExtVa != NULL) {
return(offset + (PUCHAR)MappingInfo->RemappedSrbExtVa);
}
else if (MappingInfo->RemappedSenseVa != NULL) {
return(offset + (PUCHAR)MappingInfo->RemappedSenseVa);
}
return(address);
}
VOID
ScsiPortIoMapTransferVrfy(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN PVOID LogicalAddress,
IN ULONG Length
)
{
ScsiPortIoMapTransfer(HwDeviceExtension,
Srb,
LogicalAddress,
Length);
return;
}
VOID
ScsiPortMoveMemoryVrfy(
IN PVOID WriteBuffer,
IN PVOID ReadBuffer,
IN ULONG Length
)
{
ScsiPortMoveMemory(WriteBuffer,
ReadBuffer,
Length);
return;
}
ULONG
ScsiPortSetBusDataByOffsetVrfy(
IN PVOID DeviceExtension,
IN ULONG BusDataType,
IN ULONG SystemIoBusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Offset,
IN ULONG Length
)
{
ULONG Result;
if (BusDataType != PCIConfiguration) {
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_BAD_BUSDATATYPE,
(ULONG_PTR)BusDataType,
(ULONG_PTR)DeviceExtension,
(ULONG_PTR)SystemIoBusNumber);
}
Result = ScsiPortSetBusDataByOffset(DeviceExtension,
BusDataType,
SystemIoBusNumber,
SlotNumber,
Buffer,
Offset,
Length);
return Result;
}
BOOLEAN
ScsiPortValidateRangeVrfy(
IN PVOID HwDeviceExtension,
IN INTERFACE_TYPE BusType,
IN ULONG SystemIoBusNumber,
IN SCSI_PHYSICAL_ADDRESS IoAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN InIoSpace
)
{
BOOLEAN Result;
Result = ScsiPortValidateRange(HwDeviceExtension,
BusType,
SystemIoBusNumber,
IoAddress,
NumberOfBytes,
InIoSpace);
return Result;
}
VOID
ScsiPortStallExecutionVrfy(
IN ULONG Delay
)
{
//
// Miniports must specify a delay not more than one millisecond.
//
if (Delay > SpVrfyMaximumStall) {
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_STALL_TOO_LONG,
(ULONG_PTR)Delay,
0,
0);
}
KeStallExecutionProcessor(Delay);
}
//
// Timeout periods in ticks. To calculate, we divide the time limit in 100 ns
// units by the TimeIncrement, which is the value returned by
// KeQueryTimeIncrement. Since KeQueryTickCount rounds up to the next
// tick, we'll add one tick to the defined limits.
//
#define SP_FIVE_SECOND_LIMIT ((50000000L / TimeIncrement) + 1)
#define SP_TWO_SECOND_LIMIT ((20000000L / TimeIncrement) + 1)
#define SP_HALF_SECOND_LIMIT ((5000000L / TimeIncrement) + 1)
/*++
Macro Description:
This macro checks the number of ticks elapsed during the execution of a
miniport routine against a maximum number allowed ticks. If the routine
ran longer than the max allowable ticks, we bugcheck.
Arguments:
Ticks - number of ticks routine took to execute.
MaxTicks - number of ticks the routine is allowed to execute.
Routine - address of the routine we are checking.
Extension - address of the miniport's HwDeviceExtension
Notes:
The format for the bugcheck is:
Parameter 1: 0x1002
Parameter 2: address of routine that ran too long
Parameter 3: address of miniport's HwDeviceExtension
Parameter 4: duration of routine in microseconds
--*/
/*
#define SpCheckMiniportRoutineDuration(Ticks, MaxTicks, Routine, Extension) \
{ \
if ((Ticks) > (MaxTicks)) { \
KeBugCheckEx ( \
SCSI_VERIFIER_DETECTED_VIOLATION, \
SCSIPORT_VERIFIER_MINIPORT_ROUTINE_TIMEOUT, \
(ULONG_PTR)(Routine), \
(ULONG_PTR)(Extension), \
(ULONG_PTR)(((Ticks) * TimeIncrement) / 10)); \
} \
}
*/
#define SpCheckMiniportRoutineDuration(Ticks, MaxTicks, Routine, Extension)
ULONG
SpHwFindAdapterVrfy (
IN PVOID HwDeviceExtension,
IN PVOID HwContext,
IN PVOID BusInformation,
IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again
)
{
ULONG Result;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
Result = AdapterExtension->VerifierExtension->RealHwFindAdapter(
HwDeviceExtension,
HwContext,
BusInformation,
ArgumentString,
ConfigInfo,
Again);
return Result;
}
BOOLEAN
SpHwInitializeVrfy (
IN PVOID HwDeviceExtension
)
{
BOOLEAN Result;
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
KeQueryTickCount(&Start);
Result = AdapterExtension->VerifierExtension->RealHwInitialize(HwDeviceExtension);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
if (VRFY_DO_CHECK(AdapterExtension, SP_DONT_CHK_HW_INITIALIZE_DURATION)) {
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_FIVE_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwInitialize,
HwDeviceExtension);
}
return Result;
}
BOOLEAN
SpHwStartIoVrfy (
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
{
BOOLEAN Result;
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
PSRB_DATA srbData;
//
// If MapBuffers is not set, the miniport is not supposed to touch
// the DataBuffer field in the SRB. To verify this, we'll set
// DataBuffer to point to memory that will fault if the miniport tries
// to touch it.
//
ASSERT(Srb != NULL);
srbData = (PSRB_DATA)Srb->OriginalRequest;
ASSERT_SRB_DATA(srbData);
if (AdapterExtension->MapBuffers == FALSE
&& !IS_MAPPED_SRB(Srb)
&& Srb->Function != SRB_FUNCTION_WMI
&& Srb->DataBuffer != NULL
&& AdapterExtension->VerifierExtension->InvalidPage != NULL
&& Srb->Cdb[0] != SCSIOP_INQUIRY
&& Srb->Cdb[0] != SCSIOP_REPORT_LUNS) {
if (Srb->DataBuffer != AdapterExtension->VerifierExtension->InvalidPage) {
srbData->UnmappedDataBuffer = Srb->DataBuffer;
Srb->DataBuffer = AdapterExtension->VerifierExtension->InvalidPage;
} else {
ASSERT(srbData->UnmappedDataBuffer != &SpMarker);
ASSERT(srbData->UnmappedDataBuffer != NULL);
}
} else {
srbData->UnmappedDataBuffer = &SpMarker;
}
//
// Call the miniport's StartIo function and calculate the call's duration.
//
KeQueryTickCount(&Start);
Result = AdapterExtension->VerifierExtension->RealHwStartIo(
HwDeviceExtension,
Srb);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
//
// Bugcheck if the call took more than .5 seconds.
//
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_HALF_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwStartIo,
HwDeviceExtension);
//
// If the HwStartIo returns failure, undo any fixups we performed on the SRB.
//
if (Result == FALSE
&& srbData->UnmappedDataBuffer != &SpMarker) {
ASSERT(srbData->UnmappedDataBuffer != NULL);
Srb->DataBuffer = srbData->UnmappedDataBuffer;
srbData->UnmappedDataBuffer = NULL;
}
return Result;
}
BOOLEAN
SpHwInterruptVrfy (
IN PVOID HwDeviceExtension
)
{
BOOLEAN Result;
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
if (AdapterExtension->VerifierExtension->RealHwInterrupt == NULL) {
return FALSE;
}
KeQueryTickCount(&Start);
Result = AdapterExtension->VerifierExtension->RealHwInterrupt (
HwDeviceExtension);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_HALF_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwInterrupt,
HwDeviceExtension);
return Result;
}
BOOLEAN
SpHwResetBusVrfy (
IN PVOID HwDeviceExtension,
IN ULONG PathId
)
{
BOOLEAN Result;
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
KeQueryTickCount(&Start);
Result = AdapterExtension->VerifierExtension->RealHwResetBus(
HwDeviceExtension,
PathId);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_HALF_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwResetBus,
HwDeviceExtension);
return Result;
}
VOID
SpHwDmaStartedVrfy (
IN PVOID HwDeviceExtension
)
{
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
KeQueryTickCount(&Start);
AdapterExtension->VerifierExtension->RealHwDmaStarted(
HwDeviceExtension);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_HALF_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwDmaStarted,
HwDeviceExtension);
}
BOOLEAN
SpHwRequestInterruptVrfy (
IN PVOID HwDeviceExtension
)
{
BOOLEAN Result;
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
KeQueryTickCount(&Start);
Result = AdapterExtension->VerifierExtension->RealHwRequestInterrupt(
HwDeviceExtension);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_HALF_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwRequestInterrupt,
HwDeviceExtension);
return Result;
}
BOOLEAN
SpHwTimerRequestVrfy (
IN PVOID HwDeviceExtension
)
{
BOOLEAN Result;
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
KeQueryTickCount(&Start);
Result = AdapterExtension->VerifierExtension->RealHwTimerRequest(
HwDeviceExtension);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_HALF_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwTimerRequest,
HwDeviceExtension);
return Result;
}
SCSI_ADAPTER_CONTROL_STATUS
SpHwAdapterControlVrfy (
IN PVOID HwDeviceExtension,
IN SCSI_ADAPTER_CONTROL_TYPE ControlType,
IN PVOID Parameters
)
{
SCSI_ADAPTER_CONTROL_STATUS Result;
LARGE_INTEGER Start;
LARGE_INTEGER Duration;
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
KeQueryTickCount (&Start);
Result = AdapterExtension->VerifierExtension->RealHwAdapterControl(
HwDeviceExtension,
ControlType,
Parameters);
KeQueryTickCount(&Duration);
Duration.QuadPart -= Start.QuadPart;
if (VRFY_DO_CHECK(AdapterExtension, SP_DONT_CHK_HW_ADAPTERCONTROL_DURATION)) {
SpCheckMiniportRoutineDuration(
Duration.LowPart,
SP_HALF_SECOND_LIMIT,
AdapterExtension->VerifierExtension->RealHwAdapterControl,
HwDeviceExtension);
}
return Result;
}
VOID
SpVerifySrbStatus(
PVOID HwDeviceExtension,
PSCSI_REQUEST_BLOCK srb
)
/*++
Routine Description:
Verify that the SRB's status as set by the miniport driver is valid.
Arguments:
HwDeviceExtension - The port driver's device extension follows the
miniport's device extension and contains a pointer to
the logical device extension list.
srb - Points to the SRB.
Return Value:
VOID
--*/
{
UCHAR SrbStatus;
//
// Turn off internal bits used by scsiport.
//
SrbStatus = srb->SrbStatus & ~(SRB_STATUS_QUEUE_FROZEN |
SRB_STATUS_AUTOSENSE_VALID);
//
// Miniports may never set the status to SRB_STATUS_PENDING.
//
if (SrbStatus == SRB_STATUS_PENDING) {
goto BadStatus;
}
//
// If the function is SRB_FUNCTION_EXECUTE_SCSI, then the command must be
// either completed successfully, or ScsiStatus must be set to
// SCSISTAT_GOOD.
//
if (!(SrbStatus != SRB_STATUS_SUCCESS ||
srb->ScsiStatus == SCSISTAT_GOOD ||
srb->Function != SRB_FUNCTION_EXECUTE_SCSI)) {
goto BadStatus;
}
//
// Make sure the status is within the valid range.
//
if ((SrbStatus) == 0x0C ||
(SrbStatus > 0x16 && srb->SrbStatus < 0x20) ||
(SrbStatus > 0x23)) {
goto BadStatus;
}
//
// The SRB Status is ok.
//
return;
BadStatus:
//
// Bugcheck if the status is bad.
//
KeBugCheckEx (SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_BAD_SRBSTATUS,
(ULONG_PTR)srb,
(ULONG_PTR)HwDeviceExtension,
0);
}
PVOID
SpRemapBlock(
IN PVOID BlockVa,
IN ULONG BlockSize,
OUT PMDL* Mdl
)
/*++
Routine Description:
This function attempts to remap the supplied VA range. If the block is
remapped, it will be made invalid for reading and writing.
Arguments:
BlockVa - Supplies the address of the block of memory to remap.
BlockSize - Supplies the size of the block of memory to remap.
Mdl - Supplies the address into which the function will store
a pointer to the MDL for the remapped range. If the MDL
cannot be allocated or if the range cannot be remapped,
this will be NULL upon return.
Return Value:
If the range is successfully remapped, the address of the beginning of
the remapped range is returned. Else, NULL is returned.
--*/
{
PVOID MappedRange;
NTSTATUS Status;
PMDL LocalMdl;
//
// Try to allocate a new MDL for the range we're trying to remap.
//
LocalMdl = IoAllocateMdl(BlockVa, BlockSize, FALSE, FALSE, NULL);
if (LocalMdl == NULL) {
*Mdl = NULL;
return NULL;
}
//
// Try to lock the pages. This initializes the MDL properly.
//
__try {
MmProbeAndLockPages(LocalMdl, KernelMode, IoModifyAccess);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
IoFreeMdl(LocalMdl);
*Mdl = NULL;
return NULL;
}
//
// Try to remap the range represented by the new MDL.
//
MappedRange = MmMapLockedPagesSpecifyCache(LocalMdl,
KernelMode,
MmCached,
NULL,
FALSE,
NormalPagePriority);
if (MappedRange == NULL) {
IoFreeMdl(LocalMdl);
*Mdl = NULL;
return NULL;
}
//
// If we've gotten this far, we have successfully remapped the range.
// Now we want to invalidate the entire range so any accesses to it
// will be trapped by the system.
//
Status = MmProtectMdlSystemAddress(LocalMdl, PAGE_NOACCESS);
#if DBG==1
if (!NT_SUCCESS(Status)) {
DebugPrint((0, "SpRemapBlock: failed to remap block:%p mdl:%p (%x)\n",
BlockVa, LocalMdl, Status));
}
#endif
//
// Copy the MDL we allocated into the supplied address and return the
// address of the beginning of the remapped range.
//
*Mdl = LocalMdl;
return MappedRange;
}
VOID
SpRemapCommonBufferForMiniport(
PADAPTER_EXTENSION Adapter
)
/*++
Routine Description:
This routine attempts to remap all of the common buffer blocks allocated
for a particular adapter.
Arguments:
DeviceExtension - Supplies a pointer to the adapter device extension.
--*/
{
PVOID* BlkAddr = Adapter->VerifierExtension->CommonBufferVAs;
PSP_VA_MAPPING_INFO MappingInfo;
PVOID RemappedVa;
ULONG Size;
PMDL Mdl;
ULONG i;
//
// Iterate through all of the common buffer blocks, and attempt to remap
// the SRB extension and the sense buffer within each block.
//
for (i = 0; i < Adapter->VerifierExtension->CommonBufferBlocks; i++) {
//
// Get a pointer to the mapping info we keep at the end of the block.
//
MappingInfo = GET_VA_MAPPING_INFO(Adapter, BlkAddr[i]);
//
// Initialize the original VA info for the SRB extension.
//
MappingInfo->OriginalSrbExtVa = BlkAddr[i];
MappingInfo->SrbExtLen = (ULONG)ROUND_TO_PAGES(Adapter->SrbExtensionSize);
//
// Initialize the original VA info for the sense buffer.
//
MappingInfo->OriginalSenseVa = (PUCHAR)BlkAddr[i] + MappingInfo->SrbExtLen;
MappingInfo->SenseLen = PAGE_SIZE;
//
// Try to remap the SRB extension. If successful, initialize the
// remapped VA info for the SRB extension.
//
RemappedVa = SpRemapBlock(MappingInfo->OriginalSrbExtVa,
MappingInfo->SrbExtLen,
&Mdl);
if (RemappedVa != NULL) {
MappingInfo->RemappedSrbExtVa = RemappedVa;
MappingInfo->SrbExtMdl = Mdl;
}
#if 0
//
// Try to remap the sense buffer. If successful, initialize the
// remapped VA info for the sense buffer.
//
// For now, I think we can live without this. I don't know of any
// issues where overruns etc. occur in a sense buffer.
//
RemappedVa = SpRemapBlock(MappingInfo->OriginalSenseVa,
MappingInfo->SenseLen,
&Mdl);
if (RemappedVa != NULL) {
MappingInfo->RemappedSenseVa = RemappedVa;
MappingInfo->SenseMdl = Mdl;
}
#endif
}
if (Adapter->VerifierExtension->NonCachedBufferSize != 0) {
//
// Init uncached extension mapping info.
//
Size = (ULONG)ROUND_TO_PAGES(Adapter->VerifierExtension->NonCachedBufferSize);
MappingInfo = (PSP_VA_MAPPING_INFO)((PUCHAR)BlkAddr[i] + (Size - PAGE_SIZE));
MappingInfo->OriginalSrbExtVa = BlkAddr[i];
MappingInfo->SrbExtLen = Adapter->VerifierExtension->NonCachedBufferSize;
MappingInfo->OriginalSenseVa = (PUCHAR)BlkAddr[i] + Adapter->VerifierExtension->NonCachedBufferSize;
}
}
PVOID
SpAllocateContiguousChunk(
IN PDRIVER_OBJECT DriverObject,
IN PDMA_ADAPTER DmaAdapterObject,
IN BOOLEAN Dma64BitAddresses,
IN ULONG Length,
IN ULONG Align,
OUT PHYSICAL_ADDRESS *PhysicalCommonBuffer,
OUT BOOLEAN *CommonBuffer
)
/*++
Routine Description:
This routine allocates a chunk of memory which can be used for common
buffer io. Where the memory is allocated from depends on several
parameters. If no adapter object is specified, the memory is simply
allocated from non-paged pool. Else, the memory is allocated such
that it can be used in DMA operations.
Arguments:
DriverObject - Supplies a pointer to the driver object.
DmaAdapterObject - Supplies a pointer to the adapter's DMA adapter
object.
Dma64BitAddresses - Specifies whether the adapter supports 64-bit.
Length - Specifies the number of bytes to allocate.
Align - Alignment requirement for uncached extension.
PhysicalCommonBuffer - Specifies a pointer into which the physical
address of the allocated memory is to be copied
if the memory is allocated for DMA operations.
CommonBuffer - Supplies a pointer to a boolean that we set if the
memory is allocated using AllocateCommonBuffer.
Return Value:
Returns the VA of the allocated memory if the allocation succeeds. Else,
returns NULL.
--*/
{
PVOID Buffer;
if (DmaAdapterObject == NULL) {
//
// Since there is no adapter object just allocate from non-paged pool.
//
Buffer = SpAllocatePool(
NonPagedPool,
Length,
SCSIPORT_TAG_COMMON_BUFFER,
DriverObject);
} else {
ASSERT(PhysicalCommonBuffer != NULL);
//
// If the controller can do 64-bit addresses then we need to
// specifically force the uncached extension area below the 4GB mark.
//
if (((Sp64BitPhysicalAddresses) && (Dma64BitAddresses == TRUE)) ||
Align != 0) {
PHYSICAL_ADDRESS low;
PHYSICAL_ADDRESS high;
PHYSICAL_ADDRESS boundary;
if (Align != 0) {
boundary.QuadPart = Length;
} else {
boundary.QuadPart = 0;
}
low.QuadPart = 0;
high.HighPart = 0;
high.LowPart = 0xffffffff;
//
// We'll get page aligned memory out of this which is probably
// better than the requirements of the adapter.
//
Buffer = MmAllocateContiguousMemorySpecifyCache(
Length,
low,
high,
boundary,
MmCached);
if (Buffer != NULL) {
*PhysicalCommonBuffer = MmGetPhysicalAddress(Buffer);
}
if (CommonBuffer != NULL) {
*CommonBuffer = FALSE;
}
} else {
Buffer = AllocateCommonBuffer(
DmaAdapterObject,
Length,
PhysicalCommonBuffer,
FALSE);
if (CommonBuffer != NULL) {
*CommonBuffer = TRUE;
}
}
}
return Buffer;
}
NTSTATUS
SpGetCommonBufferVrfy(
PADAPTER_EXTENSION DeviceExtension,
ULONG NonCachedExtensionSize
)
/*++
Routine Description:
This function allocates multiple common buffer blocks instead of one
big one. The verifier does this so it can remap VA ranges within
each block in order to control their protection attributes. This
enables us to invalidate key VA ranges and catch miniports that attempt
to access these ranges when they should not.
If the remapping succeeds, the SCSI port driver hands out the remapped
VA ranges to miniports instead of the original ranges. If the remapping
fails, it just hands out the original ranges.
Arguments:
DeviceExtension - Supplies a pointer to the device extension.
NonCachedExtensionSize - Supplies the size of the noncached device
extension for the miniport driver.
Return Value:
Returns the status of the allocate operation.
--*/
{
NTSTATUS Status;
PVOID buffer;
ULONG length;
ULONG blockSize;
PVOID *srbExtension;
PVOID buffer2;
PMDL mdl;
ULONG TotalSize;
ULONG i;
PVOID* BlkAddr;
PHYSICAL_ADDRESS *PhysicalCommonBuffer;
PCCHAR InvalidRegion;
BOOLEAN commonBuffer;
PAGED_CODE();
DebugPrint((1, "SpGetCommonBufferVrfy: DeviceExtension:%p NonCachedExtensionSize:%d\n",
DeviceExtension, NonCachedExtensionSize));
//
// Now fixup the size if the adapter has special alignment requirements so
// the buffer we allocate may be aligned as required.
//
if (DeviceExtension->UncachedExtAlignment != 0) {
NonCachedExtensionSize =
ROUND_UP_COUNT(NonCachedExtensionSize,
DeviceExtension->UncachedExtAlignment);
}
//
// We maintain a couple of arrays in order to find our common
// buffer blocks at various times. Calculate the amount of space we
// need for these arrays. This amount depends on the number of
// simultaneous requests the adapter supports. We add one to the
// number of requests in order to accommodate the non-cached extension.
//
ASSERT(DeviceExtension->VerifierExtension->CommonBufferVAs == NULL);
i = DeviceExtension->NumberOfRequests + 1;
length = sizeof(PVOID) * i;
if (DeviceExtension->DmaAdapterObject != NULL) {
ASSERT(DeviceExtension->VerifierExtension->CommonBufferPAs == NULL);
length += (sizeof(PHYSICAL_ADDRESS) * i);
}
//
// Allocate a block of memory for these arrays. If this allocation fails,
// we return failure.
//
BlkAddr = SpAllocatePool(NonPagedPool,
length,
SCSIPORT_TAG_COMMON_BUFFER,
DeviceExtension->DeviceObject->DriverObject);
if (BlkAddr == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Save the number of common buffer blocks.
//
DeviceExtension->VerifierExtension->CommonBufferBlocks =
DeviceExtension->NumberOfRequests;
//
// Zero the entire block so when we're freeing resources we can tell if we
// have valid buffers to free.
//
RtlZeroMemory(BlkAddr, length);
//
// Save a pointer to the array of addresses in the adapter extension and,
// if there is an adapter object, initialize a pointer to the beginning of
// the physical address array and save a pointer to the array in the
// adapter extension.
//
DeviceExtension->VerifierExtension->CommonBufferVAs = (PVOID *)BlkAddr;
if (DeviceExtension->DmaAdapterObject != NULL) {
PhysicalCommonBuffer = (PHYSICAL_ADDRESS*) &BlkAddr[i];
DeviceExtension->VerifierExtension->CommonBufferPAs = PhysicalCommonBuffer;
}
//
// To ensure that we never transfer normal request data to the SrbExtension
// (ie. the case of Srb->SenseInfoBuffer == VirtualAddress in
// ScsiPortGetPhysicalAddress) on some platforms where an inconsistency in
// MM can result in the same Virtual address supplied for 2 different
// physical addresses, bump the SrbExtensionSize if it's zero.
//
if (DeviceExtension->SrbExtensionSize == 0) {
DeviceExtension->SrbExtensionSize = 16;
}
//
// Calculate the block size for an SRB extension/sense buffer block. If
// AutoRequestSense is FALSE, allocate 1 page anyway as a placeholder.
//
blockSize = (ULONG)ROUND_TO_PAGES(DeviceExtension->SrbExtensionSize);
if (DeviceExtension->AutoRequestSense == TRUE) {
blockSize += sizeof(SENSE_DATA) + DeviceExtension->AdditionalSenseBytes;
blockSize = (ULONG)ROUND_TO_PAGES(blockSize);
} else {
blockSize += PAGE_SIZE;
}
//
// Add a page for holding bookkeeping information.
//
blockSize += PAGE_SIZE;
//
// Allocate each block individually and link them all together into a
// list. If we fail to allocate any of the blocks, we clean everything up
// and return failure.
//
DeviceExtension->CommonBufferSize = blockSize;
srbExtension = NULL;
for (i = 0; i < DeviceExtension->NumberOfRequests; i++) {
//
// Allocate a contiguous chunk of memory for the block.
//
buffer = SpAllocateContiguousChunk(
DeviceExtension->DeviceObject->DriverObject,
DeviceExtension->DmaAdapterObject,
DeviceExtension->Dma64BitAddresses,
blockSize,
0,
(DeviceExtension->DmaAdapterObject) ? &PhysicalCommonBuffer[i] : NULL,
&commonBuffer);
if (buffer == NULL) {
//
// Free everything we've allocated so far and return failure. This
// will also free the arrays we allocated at the beginning of this
// function.
//
DeviceExtension->VerifierExtension->IsCommonBuffer = commonBuffer;
SpFreeCommonBufferVrfy(DeviceExtension);
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Zero the entire block and save a pointer to it in our array.
//
RtlZeroMemory(buffer, blockSize);
BlkAddr[i] = buffer;
//
// Link the new block onto the front of the chain.
//
*((PVOID *) buffer) = srbExtension;
srbExtension = (PVOID *) buffer;
}
//
// Indicate whether the buffer was allocated as common buffer.
//
DeviceExtension->VerifierExtension->IsCommonBuffer = commonBuffer;
//
// Allocate the non-cached extension. Note that we align the uncached
// buffer on the next page boundary and allocate enough for a scratch page.
// If the allocation fails, free everything we've allocated so far and
// return failure.
//
if (NonCachedExtensionSize != 0) {
DeviceExtension->VerifierExtension->NonCachedBufferSize = NonCachedExtensionSize;
length = (ULONG)(ROUND_TO_PAGES(NonCachedExtensionSize));
BlkAddr[i] =
SpAllocateContiguousChunk(
DeviceExtension->DeviceObject->DriverObject,
DeviceExtension->DmaAdapterObject,
DeviceExtension->Dma64BitAddresses,
length,
DeviceExtension->UncachedExtAlignment,
(DeviceExtension->DmaAdapterObject) ? &PhysicalCommonBuffer[i] : NULL,
&DeviceExtension->UncachedExtensionIsCommonBuffer);
if (BlkAddr[i] == NULL) {
//
// Free everything we've allocated so far and return failure. This
// will also free the arrays we allocated at the beginning of this
// function.
//
SpFreeCommonBufferVrfy(DeviceExtension);
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Zero the entire block.
//
RtlZeroMemory(BlkAddr[i], length);
//
// Save a pointer to the beginning of the non-cached extension data.
// Note that the data is positioned such that it ends on a page
// boundary so if the miniport overwrites the buffer, the system will
// fault.
//
DeviceExtension->NonCachedExtension =
(PCCHAR)BlkAddr[i] +
(ROUND_TO_PAGES(NonCachedExtensionSize) - NonCachedExtensionSize);
} else {
DeviceExtension->NonCachedExtension = NULL;
DeviceExtension->VerifierExtension->NonCachedBufferSize = 0;
}
//
// If the miniport asked for an SRB Extension, point the SRB Extension List
// at the beginning of the list of blocks we allocated and chained together
// above.
//
if (DeviceExtension->AllocateSrbExtension == TRUE) {
DeviceExtension->SrbExtensionListHeader = srbExtension;
} else {
ASSERT(DeviceExtension->SrbExtensionListHeader == NULL);
}
//
// Create a second VA mapping of the common buffer area so we can make the
// range of addresses invalid when the miniport is not supposed to touch it.
// This will allow us to catch mis-behaving miniports.
//
SpRemapCommonBufferForMiniport(DeviceExtension);
DebugPrint((1, "SpGetCommonBufferVrfy: returning STATUS_SUCCESS\n"));
return(STATUS_SUCCESS);
}
VOID
SpFreeCommonBufferVrfy(
PADAPTER_EXTENSION Adapter
)
/*++
Routine Description:
This routine frees all of the common buffer space we've allocated for the
miniport on the supplied adapter. If only partially allocated, the routine
correctly cleans up the parts that are present. On exit, all the memory has
been freed and the associated pointers have been NULLed.
Arguments:
DeviceExtension - Supplies a pointer to the adapter device extension.
Return Value:
VOID
--*/
{
ULONG i;
PVOID* BlkAddr;
NTSTATUS Status;
PSP_VA_MAPPING_INFO MappingInfo;
ASSERT(Adapter->SrbExtensionBuffer == NULL);
if (Adapter->VerifierExtension != NULL &&
Adapter->VerifierExtension->CommonBufferVAs != NULL) {
//
// Initialize a pointer to the array of pointers we use to track and
// manage the common buffer blocks.
//
BlkAddr = Adapter->VerifierExtension->CommonBufferVAs;
//
// Cycle through the array of common memory descriptors, freeing each
// one. What we are freeing here is the SRB Extension/Sense Data
// buffers. Stop when we've deleted all the blocks.
//
for (i = 0; i < Adapter->VerifierExtension->CommonBufferBlocks && BlkAddr[i]; i++) {
//
// If there is a second VA range for the common block, free the
// MDL(s).
//
MappingInfo = GET_VA_MAPPING_INFO(Adapter, BlkAddr[i]);
if (MappingInfo->SrbExtMdl != NULL) {
MmProtectMdlSystemAddress(MappingInfo->SrbExtMdl, PAGE_READWRITE);
MmUnlockPages(MappingInfo->SrbExtMdl);
IoFreeMdl(MappingInfo->SrbExtMdl);
}
if (MappingInfo->SenseMdl != NULL) {
MmProtectMdlSystemAddress(MappingInfo->SrbExtMdl, PAGE_READWRITE);
MmUnlockPages(MappingInfo->SenseMdl);
IoFreeMdl(MappingInfo->SenseMdl);
}
//
// Free the memory. The method we use depends on how the memory
// was allocated.
//
if (Adapter->DmaAdapterObject == NULL) {
ExFreePool(BlkAddr[i]);
} else {
if (Adapter->VerifierExtension->IsCommonBuffer == FALSE) {
MmFreeContiguousMemorySpecifyCache(
BlkAddr[i],
Adapter->CommonBufferSize,
MmCached);
} else {
FreeCommonBuffer(
Adapter->DmaAdapterObject,
Adapter->CommonBufferSize,
Adapter->VerifierExtension->CommonBufferPAs[i],
BlkAddr[i],
FALSE);
}
}
}
//
// Free the uncached extension if we allocated one.
//
if (Adapter->NonCachedExtension != NULL) {
ULONG Length;
//
// Calculate the total length of the non-cached extension block we
// allocated. This is the non-cached buffer size asked for by the
// miniport rounded up to the next page boundary plus one full page.
//
Length = (ULONG)(ROUND_TO_PAGES(Adapter->VerifierExtension->NonCachedBufferSize));
//
// Free the memory. The method we use depends on how the memory
// was allocated.
//
if (Adapter->DmaAdapterObject == NULL) {
ExFreePool(BlkAddr[i]);
} else {
if (Adapter->UncachedExtensionIsCommonBuffer == FALSE) {
MmFreeContiguousMemorySpecifyCache(
BlkAddr[i],
Length,
MmCached);
} else {
FreeCommonBuffer(
Adapter->DmaAdapterObject,
Length,
Adapter->VerifierExtension->CommonBufferPAs[i],
BlkAddr[i],
FALSE);
}
}
Adapter->NonCachedExtension = NULL;
}
//
// Free the arrays we allocated to manage the common buffer area.
//
ExFreePool(Adapter->VerifierExtension->CommonBufferVAs);
Adapter->VerifierExtension->CommonBufferVAs = NULL;
Adapter->VerifierExtension->CommonBufferPAs = NULL;
Adapter->VerifierExtension->CommonBufferBlocks = 0;
Adapter->SrbExtensionListHeader = NULL;
}
}
PVOID
SpGetOriginalSrbExtVa(
PADAPTER_EXTENSION Adapter,
PVOID Va
)
/*++
Routine Description:
This function returns the original mapped virtual address of a common
block if the supplied VA is for one of the common buffer blocks we've
allocated.
Arguments:
Adapter - the adapter device extension
Va - virtual address of a common buffer block
Return Value:
If the supplied VA is the address of one of the common buffer blocks,
returns the original VA of the block. Else, returns NULL.
--*/
{
PVOID* BlkAddr = Adapter->VerifierExtension->CommonBufferVAs;
PSP_VA_MAPPING_INFO MappingInfo;
ULONG i;
for (i = 0; i < Adapter->VerifierExtension->CommonBufferBlocks; i++) {
MappingInfo = GET_VA_MAPPING_INFO(Adapter, *BlkAddr++);
if (Va == MappingInfo->RemappedSrbExtVa ||
Va == MappingInfo->OriginalSrbExtVa)
return MappingInfo->OriginalSrbExtVa;
}
return NULL;
}
VOID
SpInsertSrbExtension(
PADAPTER_EXTENSION Adapter,
PCCHAR SrbExtension
)
/*++
Routine Description:
This routine inserts the supplied SRB extension back into the SRB extension
list. The VA of the supplied extension lies within one of our common buffer
blocks and it may be a remapped VA. If it is a remapped address, this
routine invalidates the page(s) comprising the extension after it links the
extension back into the list.
Arguments:
Adapter - Pointer to an adapter device extension.
SrbExtension - Pointer to the beginning of an SRB extension within one of
our common buffer blocks. May or may not be within a
remapped range.
--*/
{
//
// Round the srb extension pointer down to the beginning of the page
// and link the block back into the list. Note that we're careful
// to point the list header at the original VA of the block.
//
SrbExtension = (PVOID)((ULONG_PTR)SrbExtension & ~(PAGE_SIZE - 1));
*((PVOID *) SrbExtension) = Adapter->SrbExtensionListHeader;
Adapter->SrbExtensionListHeader = SpGetOriginalSrbExtVa(
Adapter,
SrbExtension);
//
// If the original VA differs from the one supplied, the supplied
// one is one of our remapped VAs. In this case, we want to invalidate
// the range so the system will bugcheck if anyone tries to access it.
//
if (Adapter->SrbExtensionListHeader != SrbExtension) {
PMDL Mdl = SpGetRemappedSrbExt(Adapter, Adapter->SrbExtensionListHeader);
ASSERT(Mdl != NULL);
MmProtectMdlSystemAddress(Mdl, PAGE_NOACCESS);
//
// Just because we remapped the SRB extension does not mean we
// necessarily remapped the sense buffer.
//
Mdl = SpGetRemappedSenseBuffer(Adapter, Adapter->SrbExtensionListHeader);
if (Mdl != NULL) {
MmProtectMdlSystemAddress(Mdl, PAGE_NOACCESS);
}
}
}
#if DBG
ULONG SpVerbose = 0;
#endif
PVOID
SpPrepareSrbExtensionForUse(
IN PADAPTER_EXTENSION Adapter,
IN OUT PCCHAR *SrbExtension
)
/*++
Routine Description:
This function accepts a pointer to the beginning of one of the individual
common-buffer blocks allocated by the verifier for SRB extensions, sense
buffers, and non-cached extensions. It calculates the beginning of the
SRB extension within the block and, if the block has been remapped, makes
the page(s) of the SRB extension read/write valid.
Arguments:
Adapter - Pointer to an adapter device extension.
SrbExtension - Pointer to the beginning of a common-buffer block.
Return Value:
If the common buffer block containing the SRB extension has been remapped,
returns the address of the beginning of the remapped srb extension, valid
for reading and writing.
If the block has not been remapped, returns NULL.
Regardless of whether the block is remapped or not, the supplied pointer
is fixed up to point to the beginning of the SRB extension within the
original VA range.
--*/
{
PCCHAR RemappedSrbExt = NULL;
NTSTATUS Status;
PMDL Mdl;
ULONG srbExtensionSize = ROUND_UP_COUNT(Adapter->SrbExtensionSize, 8);
//
// If we've remapped the SRB extension, get the second mapping and make it
// valid. If we get the second mapping, but cannot make it valid, we just
// use the original mapping.
//
Mdl = SpGetRemappedSrbExt(Adapter, *SrbExtension);
if (Mdl != NULL) {
Status = MmProtectMdlSystemAddress(Mdl, PAGE_READWRITE);
if (NT_SUCCESS(Status)) {
RemappedSrbExt = MmGetSystemAddressForMdlSafe(
Mdl,
NormalPagePriority);
//
// Adjust the remapped srb extension pointer so the end of the
// buffer falls on a page boundary.
//
RemappedSrbExt +=
((Adapter->CommonBufferSize -
(PAGE_SIZE * 2)) - srbExtensionSize);
}
}
//
// Adjust the original srb extension pointer so it also ends on a page
// boundary.
//
*SrbExtension += ((Adapter->CommonBufferSize - (PAGE_SIZE * 2)) -
srbExtensionSize);
#if DBG
if (SpVerbose == 1) {
DebugPrint((0, "SpPrepareSrbExtensionForUse: SrbExt %p SrbExtSize %x\n",
*SrbExtension, srbExtensionSize));
}
#endif
return RemappedSrbExt;
}
PCCHAR
SpPrepareSenseBufferForUse(
PADAPTER_EXTENSION Adapter,
PCCHAR SrbExtension
)
/*++
Routine Description:
This function accepts a pointer to the beginning of an SRB extension
within one of the individual common-buffer blocks allocated by the
verifier for SRB extensions, sense buffers, and non-cached extensions.
It calculates the beginning of the sense buffer within the block and,
if the block has been remapped, makes the page read/write valid.
It is assumed that a sense buffer will never be larger than one page.
Arguments:
Adapter - Pointer to an adapter device extension.
SrbExtension - Pointer to the beginning of the SRB extension within a
common-buffer block.
Return Value:
Returns the address of the beginning of a sense buffer valid for
reading and writing.
--*/
{
PVOID BeginningOfBlock;
ULONG SenseDataSize;
PCCHAR Base;
NTSTATUS Status;
PMDL Mdl;
ULONG srbExtensionSize = (ULONG)ROUND_TO_PAGES(Adapter->SrbExtensionSize);
//
// Initialize the size of the sense buffer and the base of the sense buffer
// within the originally allocated block. The base of the sense buffer
// immediately follows the srb extension and resides on a page boundary
// within a common buffer block.
//
SenseDataSize = sizeof(SENSE_DATA) + Adapter->AdditionalSenseBytes;
SenseDataSize = ROUND_UP_COUNT(SenseDataSize, 8);
Base = SrbExtension + ROUND_UP_COUNT(Adapter->SrbExtensionSize, 8);
//
// Initialize a pointer to the beginning of the common block the sense
// buffer resides in. This is needed in order to determine if the
// sense buffer has been remapped.
//
BeginningOfBlock =
(PVOID)(((ULONG_PTR)SrbExtension +
ROUND_UP_COUNT(Adapter->SrbExtensionSize, 8)) -
srbExtensionSize);
//
// If we've remapped the sense buffer, make the range valid and reset base
// to point to the beginning of the range.
//
Mdl = SpGetRemappedSenseBuffer(Adapter, BeginningOfBlock);
if (Mdl != NULL) {
Status = MmProtectMdlSystemAddress(Mdl, PAGE_READWRITE);
if (NT_SUCCESS(Status)) {
Base = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
ASSERT(Base != NULL);
}
}
#if DBG
if (SpVerbose == 1) {
DebugPrint((0, "SpPrepareSenseBufferForUse: SrbExt %p Base %p BOB %p "
"SenseBuffer %p SrbExtSize %x\n",
SrbExtension,
Base,
BeginningOfBlock,
(Base + PAGE_SIZE - SenseDataSize),
srbExtensionSize));
}
#endif
//
// Return a pointer into the block such that the sense buffer ends aligned
// on a page boundary.
//
return (Base + PAGE_SIZE - SenseDataSize);
}
PVOID
SpGetInaccessiblePage(
PADAPTER_EXTENSION Adapter
)
/*++
Routine Description:
This function returns a pointer to a page of memory that is not valid
for reading or writing. This page is a shared resource, used by all
adapters that are actively verifying. The page is hung off of the driver
extension. The page is faulted in as needed, so if we haven't initialized
it yet, we try to do so here in an interlocked fashion.
Arguments:
Adapter - Pointer to an adapter device extension.
Return Value:
Either returns a pointer to an invalid page of VAs or NULL if the page
could not be allocated.
--*/
{
PSCSIPORT_DRIVER_EXTENSION DriverExtension;
PVOID UnusedPage;
PVOID InvalidPage;
PMDL UnusedPageMdl;
PVOID CurrentValue;
//
// Retrieve the driver extension. We must have it to proceed.
//
DriverExtension = IoGetDriverObjectExtension(
Adapter->DeviceObject->DriverObject,
ScsiPortInitialize);
if (DriverExtension == NULL) {
return NULL;
}
//
// If the invalid page is not yet initialized, go ahead and try to
// initialize it now.
//
if (DriverExtension->InvalidPage == NULL) {
//
// Allocate a page of memory.
//
UnusedPage = SpAllocatePool(NonPagedPool,
PAGE_SIZE,
SCSIPORT_TAG_VERIFIER,
Adapter->DeviceObject->DriverObject);
if (UnusedPage != NULL) {
//
// Zero the page and remap it. The remapped range will be inaccessible.
// If the remapping fails, just free the page; we just won't have an
// inaccessible page to work with.
//
RtlZeroMemory(UnusedPage, PAGE_SIZE);
InvalidPage = SpRemapBlock(UnusedPage,
PAGE_SIZE,
&UnusedPageMdl);
if (InvalidPage != NULL) {
//
// If nobody else has beaten us to it, init the pointer to the
// invalid page in the driver extension. If somebody has already
// done it, just free the page we created. This page is freed
// when scsiport is unloaded.
//
CurrentValue = InterlockedCompareExchangePointer(
&DriverExtension->InvalidPage,
InvalidPage,
NULL);
if (CurrentValue == NULL) {
DriverExtension->UnusedPage = UnusedPage;
DriverExtension->UnusedPageMdl = UnusedPageMdl;
} else {
MmProtectMdlSystemAddress(UnusedPageMdl, PAGE_READWRITE);
UnusedPageMdl->MdlFlags &= ~MDL_MAPPED_TO_SYSTEM_VA;
IoFreeMdl(UnusedPageMdl);
ExFreePool(UnusedPage);
}
} else {
//
// Couldn't make the page inaccessible, just free it.
//
ExFreePool(UnusedPage);
}
}
}
return DriverExtension->InvalidPage;
}
BOOLEAN
SpCheckForActiveRequests(
PADAPTER_EXTENSION Adapter
)
/*++
Routine Description:
This function walks through all of the logical units connected to the
supplied adapter looking for any outstanding requests. If it finds
any, it returns TRUE immediately.
Arguments:
Adapter - Pointer to an adapter device extension.
Return Value:
TRUE - If an outstanding requests is found on one of the logical units
connected to the adapter.
FALSE - If no outstanding requests on the adapter.
--*/
{
PLOGICAL_UNIT_EXTENSION LogicalUnit;
PLOGICAL_UNIT_BIN Bin;
ULONG BinNumber;
//
// Iterate through each LU bin. For each bin, if there are any LUs, iterate
// through each of those looking for an oustanding request. If we find one
// terminate the search and return TRUE.
//
for (BinNumber = 0; BinNumber < NUMBER_LOGICAL_UNIT_BINS; BinNumber++) {
Bin = &Adapter->LogicalUnitList[BinNumber];
LogicalUnit = Bin->List;
while (LogicalUnit != NULL) {
if (LogicalUnit->AbortSrb != NULL &&
LogicalUnit->AbortSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
return TRUE;
} else if (LogicalUnit->CurrentUntaggedRequest != NULL &&
LogicalUnit->CurrentUntaggedRequest->CurrentSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
return TRUE;
} else if (LogicalUnit->RequestList.Flink != &LogicalUnit->RequestList) {
PSRB_DATA srbData;
PVOID nextEntry = LogicalUnit->RequestList.Flink;
while (nextEntry != &LogicalUnit->RequestList) {
srbData = CONTAINING_RECORD(nextEntry, SRB_DATA, RequestList);
if (srbData->CurrentSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
return TRUE;
}
nextEntry = srbData->RequestList.Flink;
}
}
LogicalUnit = LogicalUnit->NextLogicalUnit;
}
}
return FALSE;
}
VOID
SpEnsureAllRequestsAreComplete(
PADAPTER_EXTENSION Adapter
)
/*++
Routine Description:
This routine bugchecks the system if there are any outstanding requests
on the supplied adapter. If the SP_DONT_CHK_REQUESTS_ON_RESET bit is
set on the adapter's verification level, don't do the check.
Arguments:
Adapter - Points to an adapter device extension.
--*/
{
//
// If there are any outstanding requests on any of the LUs connected to the
// adapter, bugcheck the system. Note that we only do this check if it
// has not been turned off.
//
if (VRFY_DO_CHECK(Adapter, SP_DONT_CHK_REQUESTS_ON_RESET)) {
BOOLEAN ActiveRequests = SpCheckForActiveRequests(Adapter);
if (ActiveRequests == TRUE) {
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
SCSIPORT_VERIFIER_RQSTS_NOT_COMPLETE,
(ULONG_PTR)Adapter,
(ULONG_PTR)Adapter->HwDeviceExtension,
0);
}
}
}
VOID
SpDoVerifierInit(
IN PADAPTER_EXTENSION Adapter,
IN PHW_INITIALIZATION_DATA HwInitializationData
)
/*++
Routine Description:
This routine allocates and initializes a verifier extension for the
supplied adapter. A per-adapter verification level is read from the
registry before allocating the extension. A verfication level of -1
means "don't verify this adapter". If we do allocate the extension,
we also lock the verifier code section into memory.
Arguments:
Adapter - The adapter device extension.
HwInitializationData - A pointer to the HW_INITIALIZATION_DATA for
the adapter.
--*/
{
ULONG VerifyLevel;
NTSTATUS Status;
PAGED_CODE();
//
// Read adapter's verification level from the registry. If the adapter is
// configured for no verification, just return.
//
VerifyLevel = SpGetAdapterVerifyLevel(Adapter);
if (VerifyLevel == SP_VRFY_NONE) {
return;
}
//
// Go ahead and try to allocate the extension.
//
Adapter->VerifierExtension =
SpAllocatePool(NonPagedPool,
sizeof(VERIFIER_EXTENSION),
SCSIPORT_TAG_VERIFIER,
Adapter->DeviceObject->DriverObject);
if (Adapter->VerifierExtension != NULL) {
//
// Zero the extension.
//
RtlZeroMemory(Adapter->VerifierExtension, sizeof(VERIFIER_EXTENSION));
//
// Lock the pageable verifier code section into memory.
//
#ifdef ALLOC_PRAGMA
if (VerifierCodeSectionHandle == NULL) {
VerifierCodeSectionHandle = MmLockPagableCodeSection(SpHwFindAdapterVrfy);
} else {
MmLockPagableSectionByHandle(VerifierCodeSectionHandle);
}
#endif
//
// Set the verification level for this adapter. This value is the sum
// of the global verifier level and the per-adapter value we read above.
//
Adapter->VerifierExtension->VrfyLevel = (VerifyLevel | SpVrfyLevel);
//
// Initialize function pointers in the verifier extension to
// to point to the real miniport routines.
//
Adapter->VerifierExtension->RealHwFindAdapter = HwInitializationData->HwFindAdapter;
Adapter->VerifierExtension->RealHwInitialize = HwInitializationData->HwInitialize;
Adapter->VerifierExtension->RealHwStartIo = HwInitializationData->HwStartIo;
Adapter->VerifierExtension->RealHwInterrupt = HwInitializationData->HwInterrupt;
Adapter->VerifierExtension->RealHwResetBus = HwInitializationData->HwResetBus;
Adapter->VerifierExtension->RealHwDmaStarted = HwInitializationData->HwDmaStarted;
Adapter->VerifierExtension->RealHwAdapterControl = HwInitializationData->HwAdapterControl;
//
// Redirect the miniport routines to verifier routines.
//
Adapter->HwFindAdapter = SpHwFindAdapterVrfy;
Adapter->HwInitialize = SpHwInitializeVrfy;
Adapter->HwStartIo = SpHwStartIoVrfy;
Adapter->HwInterrupt = SpHwInterruptVrfy;
Adapter->HwResetBus = SpHwResetBusVrfy;
Adapter->HwDmaStarted = SpHwDmaStartedVrfy;
Adapter->HwAdapterControl = SpHwAdapterControlVrfy;
//
// Get a pointer to an invalid page of memory so we can catch
// miniports trying to touch memory when they shouldn't be.
//
Adapter->VerifierExtension->InvalidPage = SpGetInaccessiblePage(Adapter);
}
}
VOID
SpDoVerifierCleanup(
IN PADAPTER_EXTENSION Adapter
)
/*++
Routine Description:
This routine frees the supplied adapter's verifier extension and releases
its reference on the verifier code section.
This routine gets called as part of the adapter resource cleanup. When
called, all the actual resources allocated for the verifier have already
been cleaned up.
Arguments:
Adapter - the adapter device extension
--*/
{
//
// We should never arrive here if the scsiport verifier is not active.
// And when we get here we should have freed all the resources hanging
// off the extension.
//
ASSERT(Adapter->VerifierExtension != NULL);
ASSERT(Adapter->VerifierExtension->CommonBufferVAs == NULL);
ASSERT(Adapter->VerifierExtension->CommonBufferPAs == NULL);
ASSERT(Adapter->VerifierExtension->CommonBufferBlocks == 0);
//
// Free and NULL the verifier extension for this adapter.
//
ExFreePool(Adapter->VerifierExtension);
Adapter->VerifierExtension = NULL;
//
// Release our reference on the verifier code section.
//
#ifdef ALLOC_PRAGMA
ASSERT(VerifierCodeSectionHandle != NULL);
MmUnlockPagableImageSection(VerifierCodeSectionHandle);
#endif
}
ULONG
SpGetAdapterVerifyLevel(
IN PADAPTER_EXTENSION Adapter
)
/*++
Routine Description:
This function returns the verification level for the supplied adapter.
Arguments:
Adapter - Pointer to an adapter device extension.
Return Value:
The supplied adapter's verification level.
--*/
{
PSCSIPORT_DRIVER_EXTENSION DrvExt;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UnicodeString;
HANDLE ParametersKey;
HANDLE ServiceKey;
ULONG VerifyLevel = 0;
NTSTATUS Status;
PAGED_CODE();
//
// We need the driver extension to get the adapter's registry path. We use
// this to look up the adapter settings in the registry. If we cannot get
// the driver extension, we have to abort.
//
DrvExt = IoGetDriverObjectExtension(
Adapter->DeviceObject->DriverObject,
ScsiPortInitialize);
if (DrvExt == NULL) {
return 0;
}
//
// Try to open the adapter's registry key.
//
InitializeObjectAttributes(
&ObjectAttributes,
&DrvExt->RegistryPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&ServiceKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(Status)) {
//
// Try to open the adapter's parameters key.
//
RtlInitUnicodeString(&UnicodeString, L"Parameters");
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
ServiceKey,
NULL);
Status = ZwOpenKey(&ParametersKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(Status)) {
//
// Try to read the verification level value under the adapter's
// parameters key.
//
RtlInitUnicodeString(&UnicodeString, L"VerifyLevel");
SpReadNumericValue(
ParametersKey,
NULL,
&UnicodeString,
&VerifyLevel);
ZwClose(ParametersKey);
}
ZwClose(ServiceKey);
}
return VerifyLevel;
}