/*++ Copyright (C) Microsoft Corporation, 1990 - 1999 Module Name: port.c Abstract: This is the NT SCSI port driver. Authors: Mike Glass Jeff Havens Environment: kernel mode only Notes: This module is a dll for the kernel. Revision History: --*/ #include "port.h" #if DBG static const char *__file__ = __FILE__; #endif #define __FILE_ID__ 'util' typedef struct SP_GUID_INTERFACE_MAPPING { GUID Guid; INTERFACE_TYPE InterfaceType; } SP_GUID_INTERFACE_MAPPING, *PSP_GUID_INTERFACE_MAPPING; PSP_GUID_INTERFACE_MAPPING SpGuidInterfaceMappingList = NULL; VOID SpProcessSpecialControllerList( IN PDRIVER_OBJECT DriverObject, IN PINQUIRYDATA InquiryData, IN HANDLE ListKey, OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags ); VOID SpProcessSpecialControllerFlags( IN HANDLE FlagsKey, OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags ); NTSTATUS ScsiPortBuildMultiString( IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING MultiString, ... ); NTSTATUS SpMultiStringToStringArray( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING MultiString, OUT PWSTR *StringArray[], BOOLEAN Forward ); VOID FASTCALL SpFreeSrbData( IN PADAPTER_EXTENSION Adapter, IN PSRB_DATA SrbData ); VOID FASTCALL SpFreeBypassSrbData( IN PADAPTER_EXTENSION Adapter, IN PSRB_DATA SrbData ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, ScsiPortBuildMultiString) #pragma alloc_text(PAGE, ScsiPortStringArrayToMultiString) #pragma alloc_text(PAGE, SpMultiStringToStringArray) #pragma alloc_text(PAGE, RtlDuplicateCmResourceList) #pragma alloc_text(PAGE, RtlSizeOfCmResourceList) #pragma alloc_text(PAGE, SpTranslateResources) #pragma alloc_text(PAGE, SpCheckSpecialDeviceFlags) #pragma alloc_text(PAGE, SpProcessSpecialControllerList) #pragma alloc_text(PAGE, SpProcessSpecialControllerFlags) #pragma alloc_text(PAGE, SpAllocateTagBitMap) #pragma alloc_text(PAGE, SpGetPdoInterfaceType) #pragma alloc_text(PAGE, SpReadNumericInstanceValue) #pragma alloc_text(PAGE, SpWriteNumericInstanceValue) #pragma alloc_text(PAGE, SpReleaseMappedAddresses) #pragma alloc_text(PAGE, SpInitializeGuidInterfaceMapping) #pragma alloc_text(PAGE, SpSendIrpSynchronous) #pragma alloc_text(PAGE, SpGetBusTypeGuid) #pragma alloc_text(PAGE, SpDetermine64BitSupport) #pragma alloc_text(PAGE, SpReadNumericValue) #pragma alloc_text(PAGE, SpAllocateAddressMapping) #pragma alloc_text(PAGE, SpPreallocateAddressMapping) #pragma alloc_text(PAGE, SpPurgeFreeMappedAddressList) #pragma alloc_text(PAGE, SpFreeMappedAddress) #endif NTSTATUS SpInitializeGuidInterfaceMapping( IN PDRIVER_OBJECT DriverObject ) { ULONG size; PAGED_CODE(); ASSERT(SpGuidInterfaceMappingList == NULL); size = sizeof(SP_GUID_INTERFACE_MAPPING) * 5; SpGuidInterfaceMappingList = SpAllocatePool(PagedPool, size, SCSIPORT_TAG_INTERFACE_MAPPING, DriverObject); if(SpGuidInterfaceMappingList == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(SpGuidInterfaceMappingList, size); SpGuidInterfaceMappingList[0].Guid = GUID_BUS_TYPE_PCMCIA; SpGuidInterfaceMappingList[0].InterfaceType = Isa; SpGuidInterfaceMappingList[1].Guid = GUID_BUS_TYPE_PCI; SpGuidInterfaceMappingList[1].InterfaceType = PCIBus; SpGuidInterfaceMappingList[2].Guid = GUID_BUS_TYPE_ISAPNP; SpGuidInterfaceMappingList[2].InterfaceType = Isa; SpGuidInterfaceMappingList[3].Guid = GUID_BUS_TYPE_EISA; SpGuidInterfaceMappingList[3].InterfaceType = Eisa; SpGuidInterfaceMappingList[4].InterfaceType = InterfaceTypeUndefined; return STATUS_SUCCESS; } NTSTATUS ScsiPortBuildMultiString( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING MultiString, ... ) /*++ Routine Description: This routine will take a null terminated list of ascii strings and combine them together into a unicode multi-string block. This routine allocates memory for the string buffer - is the caller's responsibility to free it. Arguments: MultiString - a UNICODE_STRING structure into which the multi string will be built. ... - a NULL terminated list of narrow strings which will be combined together. This list may not be empty. Return Value: status --*/ { PCSTR rawEntry; ANSI_STRING ansiEntry; UNICODE_STRING unicodeEntry; PWSTR unicodeLocation; ULONG multiLength = 0; NTSTATUS status; va_list ap; va_start(ap, MultiString); PAGED_CODE(); // // Make sure we aren't going to leak any memory // ASSERT(MultiString->Buffer == NULL); rawEntry = va_arg(ap, PCSTR); while(rawEntry != NULL) { RtlInitAnsiString(&ansiEntry, rawEntry); multiLength += RtlAnsiStringToUnicodeSize(&ansiEntry); rawEntry = va_arg(ap, PCSTR); } ASSERT(multiLength != 0); multiLength += sizeof(WCHAR); MultiString->Buffer = SpAllocatePool(PagedPool, multiLength, SCSIPORT_TAG_PNP_ID, DriverObject); if(MultiString->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } unicodeEntry.Buffer = MultiString->Buffer; unicodeEntry.MaximumLength = (USHORT) multiLength; va_start(ap, MultiString); rawEntry = va_arg(ap, PCSTR); while(rawEntry != NULL) { RtlInitAnsiString(&ansiEntry, rawEntry); status = RtlAnsiStringToUnicodeString( &unicodeEntry, &ansiEntry, FALSE); // // Since we're not allocating any memory the only failure possible // is if this function is bad // ASSERT(NT_SUCCESS(status)); // // Push the buffer location up and reduce the maximum count // unicodeEntry.Buffer += unicodeEntry.Length + sizeof(WCHAR); unicodeEntry.MaximumLength -= unicodeEntry.Length + sizeof(WCHAR); rawEntry = va_arg(ap, PCSTR); }; ASSERT(unicodeEntry.MaximumLength == sizeof(WCHAR)); // // Stick the final NUL on the end of the multisz // RtlZeroMemory(unicodeEntry.Buffer, unicodeEntry.MaximumLength); return STATUS_SUCCESS; } NTSTATUS ScsiPortStringArrayToMultiString( IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING MultiString, PCSTR StringArray[] ) /*++ Routine Description: This routine will take a null terminated array of ascii strings and merge them together into a unicode multi-string block. This routine allocates memory for the string buffer - is the caller's responsibility to free it. Arguments: MultiString - a UNICODE_STRING structure into which the multi string will be built. StringArray - a NULL terminated list of narrow strings which will be combined together. This list may not be empty. Return Value: status --*/ { ANSI_STRING ansiEntry; UNICODE_STRING unicodeEntry; PWSTR unicodeLocation; UCHAR i; NTSTATUS status; PAGED_CODE(); // // Make sure we aren't going to leak any memory // ASSERT(MultiString->Buffer == NULL); RtlInitUnicodeString(MultiString, NULL); for(i = 0; StringArray[i] != NULL; i++) { RtlInitAnsiString(&ansiEntry, StringArray[i]); MultiString->Length += (USHORT) RtlAnsiStringToUnicodeSize(&ansiEntry); } ASSERT(MultiString->Length != 0); MultiString->MaximumLength = MultiString->Length + sizeof(UNICODE_NULL); MultiString->Buffer = SpAllocatePool(PagedPool, MultiString->MaximumLength, SCSIPORT_TAG_PNP_ID, DriverObject); if(MultiString->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(MultiString->Buffer, MultiString->MaximumLength); unicodeEntry = *MultiString; for(i = 0; StringArray[i] != NULL; i++) { RtlInitAnsiString(&ansiEntry, StringArray[i]); status = RtlAnsiStringToUnicodeString( &unicodeEntry, &ansiEntry, FALSE); // // Since we're not allocating any memory the only failure possible // is if this function is bad // ASSERT(NT_SUCCESS(status)); // // Push the buffer location up and reduce the maximum count // ((PSTR) unicodeEntry.Buffer) += unicodeEntry.Length + sizeof(WCHAR); unicodeEntry.MaximumLength -= unicodeEntry.Length + sizeof(WCHAR); }; // // Stick the final NUL on the end of the multisz // // RtlZeroMemory(unicodeEntry.Buffer, unicodeEntry.MaximumLength); return STATUS_SUCCESS; } NTSTATUS SpMultiStringToStringArray( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING MultiString, OUT PWSTR *StringArray[], BOOLEAN Forward ) { ULONG stringCount = 0; ULONG stringNumber; ULONG i; PWSTR *stringArray; PAGED_CODE(); // // Pass one: count the number of string elements. // for(i = 0; i < (MultiString->MaximumLength / sizeof(WCHAR)); i++) { if(MultiString->Buffer[i] == UNICODE_NULL) { stringCount++; } } // // Allocate the memory for a NULL-terminated string array. // stringArray = SpAllocatePool(PagedPool, (stringCount + 1) * sizeof(PWSTR), SCSIPORT_TAG_PNP_ID, DriverObject); if(stringArray == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(stringArray, (stringCount + 1) * sizeof(PWSTR)); // // Pass two : Put the string pointers in place. // i = 0; for(stringNumber = 0; stringNumber < stringCount; stringNumber++) { ULONG arrayNumber; if(Forward) { arrayNumber = stringNumber; } else { arrayNumber = stringCount - stringNumber - 1; } // // Put a pointer to the head of the string into the array. // stringArray[arrayNumber] = &MultiString->Buffer[i]; // // Scan for the end of the string. // while((i < (MultiString->MaximumLength / sizeof(WCHAR))) && (MultiString->Buffer[i] != UNICODE_NULL)) { i++; } // // Jump past the NULL. // i++; } *StringArray = stringArray; return STATUS_SUCCESS; } PCM_RESOURCE_LIST RtlDuplicateCmResourceList( IN PDRIVER_OBJECT DriverObject, POOL_TYPE PoolType, PCM_RESOURCE_LIST ResourceList, ULONG Tag ) /*++ Routine Description: This routine will attempt to allocate memory to copy the supplied resource list. If sufficient memory cannot be allocated then the routine will return NULL. Arguments: PoolType - the type of pool to allocate the duplicate from ResourceList - the resource list to be copied Tag - a value to tag the memory allocation with. If 0 then untagged memory will be allocated. Return Value: an allocated copy of the resource list (caller must free) or NULL if memory could not be allocated. --*/ { ULONG size = sizeof(CM_RESOURCE_LIST); PVOID buffer; PAGED_CODE(); size = RtlSizeOfCmResourceList(ResourceList); buffer = SpAllocatePool(PoolType, size, Tag, DriverObject); if (buffer != NULL) { RtlCopyMemory(buffer, ResourceList, size); } return buffer; } ULONG RtlSizeOfCmResourceList( IN PCM_RESOURCE_LIST ResourceList ) /*++ Routine Description: This routine returns the size of a CM_RESOURCE_LIST. Arguments: ResourceList - the resource list to be copied Return Value: an allocated copy of the resource list (caller must free) or NULL if memory could not be allocated. --*/ { ULONG size = sizeof(CM_RESOURCE_LIST); ULONG i; PAGED_CODE(); for(i = 0; i < ResourceList->Count; i++) { PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor = &(ResourceList->List[i]); ULONG j; // // First descriptor is included in the size of the resource list. // if(i != 0) { size += sizeof(CM_FULL_RESOURCE_DESCRIPTOR); } for(j = 0; j < fullDescriptor->PartialResourceList.Count; j++) { // // First descriptor is included in the size of the partial list. // if(j != 0) { size += sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); } } } return size; } #if !defined(NO_LEGACY_DRIVERS) BOOLEAN SpTranslateResources( IN PDRIVER_OBJECT DriverObject, IN PCM_RESOURCE_LIST AllocatedResources, OUT PCM_RESOURCE_LIST *TranslatedResources ) /*++ Routine Description: This routine will call into the Hal to translate any recognizable resources in the AllocatedResources list. This routine allocates the space for the translated list - the caller is responsible for freeing this buffer. If any errors occur the TranslatedResources will be NULL and the routine will return FALSE. Arguments: AllocatedResources - The list of resources to be translated. TranslatedResources - A location to store the translated resources. There will be a one to one mapping between translated and untranslated. Any non-standard resource types will be blindly copied. Return Value: TRUE if all resources were translated properly. FALSE otherwise. --*/ { PCM_RESOURCE_LIST list; ULONG listNumber; PAGED_CODE(); (*TranslatedResources) = NULL; list = RtlDuplicateCmResourceList(DriverObject, NonPagedPool, AllocatedResources, SCSIPORT_TAG_RESOURCE_LIST); if(list == NULL) { return FALSE; } for(listNumber = 0; listNumber < list->Count; listNumber++) { PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor; ULONG resourceNumber; fullDescriptor = &(list->List[listNumber]); for(resourceNumber = 0; resourceNumber < fullDescriptor->PartialResourceList.Count; resourceNumber++) { PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor; CM_PARTIAL_RESOURCE_DESCRIPTOR tmp; partialDescriptor = &(fullDescriptor->PartialResourceList.PartialDescriptors[resourceNumber]); switch(partialDescriptor->Type) { case CmResourceTypePort: case CmResourceTypeMemory: { ULONG addressSpace; if(partialDescriptor->Type == CmResourceTypePort) { addressSpace = 1; } else { addressSpace = 0; } tmp = *partialDescriptor; if(HalTranslateBusAddress( fullDescriptor->InterfaceType, fullDescriptor->BusNumber, partialDescriptor->u.Generic.Start, &addressSpace, &(tmp.u.Generic.Start))) { tmp.Type = (addressSpace == 0) ? CmResourceTypeMemory : CmResourceTypePort; } else { ExFreePool(list); return FALSE; } break; } case CmResourceTypeInterrupt: { tmp = *partialDescriptor; tmp.u.Interrupt.Vector = HalGetInterruptVector( fullDescriptor->InterfaceType, fullDescriptor->BusNumber, partialDescriptor->u.Interrupt.Level, partialDescriptor->u.Interrupt.Vector, &((UCHAR) tmp.u.Interrupt.Level), &(tmp.u.Interrupt.Affinity)); if(tmp.u.Interrupt.Affinity == 0) { // // Translation failed. // ExFreePool(list); return FALSE; } break; } }; *partialDescriptor = tmp; } } *TranslatedResources = list; return TRUE; } #endif // NO_LEGACY_DRIVERS BOOLEAN SpFindAddressTranslation( IN PADAPTER_EXTENSION AdapterExtension, IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PHYSICAL_ADDRESS RangeStart, IN ULONG RangeLength, IN BOOLEAN InIoSpace, IN OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Translation ) /*++ Routine Description: This routine will search the resource lists in the AdapterExtension to translate the given memory or i/o range using the resources provided by pnp or the Hal. Arguments: AdapterExtesnion - the device extension for the adapter making the request RangeStart - the starting address of the memory range RangeLength - the number of bytes in the memory range InIoSpace - whether the untranslated range is in io or memory space. Return Value: a pointer to a partial resource descriptor describing the proper range to be used or NULL if no matching range of sufficient length can be found. --*/ { PCM_RESOURCE_LIST list; ULONG listNumber; list = AdapterExtension->AllocatedResources; ASSERT(!AdapterExtension->IsMiniportDetected); ASSERT(AdapterExtension->AllocatedResources); ASSERT(AdapterExtension->TranslatedResources); for(listNumber = 0; listNumber < list->Count; listNumber++) { PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor; ULONG resourceNumber; fullDescriptor = &(list->List[listNumber]); if((fullDescriptor->InterfaceType != BusType) || (fullDescriptor->BusNumber != BusNumber)) { continue; } for(resourceNumber = 0; resourceNumber < fullDescriptor->PartialResourceList.Count; resourceNumber++) { PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor; UCHAR desiredType = InIoSpace ? CmResourceTypePort : CmResourceTypeMemory; partialDescriptor = &(fullDescriptor->PartialResourceList.PartialDescriptors[resourceNumber]); if(partialDescriptor->Type == desiredType) { ULONGLONG requestedStart = (ULONGLONG) RangeStart.QuadPart; ULONGLONG requestedEnd = ((ULONGLONG) RangeStart.QuadPart) + RangeLength; ULONGLONG testStart = (ULONGLONG) partialDescriptor->u.Generic.Start.QuadPart; ULONGLONG testEnd = testStart + partialDescriptor->u.Generic.Length; ULONGLONG requestedOffset = requestedStart - testStart; ULONG rangeOffset; // // Make sure the base address is within the current range. // if((requestedStart < testStart) || (requestedStart >= testEnd)) { continue; } // // Make sure the end of the requested range is within this // descriptor. // if(requestedEnd > testEnd) { continue; } // // We seem to have found a match. Copy the equivalent resource // in the translated resource list. // *Translation = AdapterExtension->TranslatedResources->List[listNumber]. PartialResourceList.PartialDescriptors[resourceNumber]; // // Return an offset into the translated range equivalent to the // offset in the untranslated range. // requestedStart = Translation->u.Generic.Start.QuadPart; requestedStart += requestedOffset; Translation->u.Generic.Start.QuadPart = requestedStart; return TRUE; }; } } return FALSE; } NTSTATUS SpLockUnlockQueue( IN PDEVICE_OBJECT LogicalUnit, IN BOOLEAN LockQueue, IN BOOLEAN BypassLockedQueue ) /*++ Routine Description: This routine will lock or unlock the logical unit queue. This routine is synchronous. Arguments: LogicalUnit - the logical unit to be locked or unlocked LockQueue - whether the queue should be locked or unlocked BypassLockedQueue - whether the operation should bypass other locks or whether it should sit in the queue. Must be true for unlock requests. Return Value: STATUS_SUCCESS if the operation was successful error status otherwise. --*/ { PLOGICAL_UNIT_EXTENSION luExtension = LogicalUnit->DeviceExtension; PIRP irp; PIO_STACK_LOCATION irpStack; PSCSI_REQUEST_BLOCK srb; PKEVENT event = NULL; NTSTATUS status; ASSERTMSG("Must bypass locked queue when unlocking: ", (LockQueue || BypassLockedQueue)); DebugPrint((1, "SpLockUnlockQueue: %sing queue for logical unit extension " "%#p\n", LockQueue ? "Lock" : "Unlock", luExtension)); // // Build an IRP to send to the logical unit. We need one stack // location for our completion routine and one for the irp to be // processed with. This irp should never be dispatched to the // irp = SpAllocateIrp((CCHAR) (LogicalUnit->StackSize + 1), FALSE, LogicalUnit->DriverObject); if(irp == NULL) { DebugPrint((1, "SpLockUnlockQueue: Couldn't allocate IRP\n")); return STATUS_INSUFFICIENT_RESOURCES; } try { srb = SpAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK), SCSIPORT_TAG_ENABLE, LogicalUnit->DriverObject); if(srb == NULL) { DebugPrint((1, "SpLockUnlockQueue: Couldn't allocate SRB\n")); status = STATUS_INSUFFICIENT_RESOURCES; leave; } event = SpAllocatePool(NonPagedPool, sizeof(KEVENT), SCSIPORT_TAG_EVENT, LogicalUnit->DriverObject); if(event == NULL) { DebugPrint((1, "SpLockUnlockQueue: Couldn't allocate Context\n")); status = STATUS_INSUFFICIENT_RESOURCES; leave; } RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); KeInitializeEvent(event, NotificationEvent, FALSE); srb->Length = sizeof(SCSI_REQUEST_BLOCK); srb->Function = LockQueue ? SRB_FUNCTION_LOCK_QUEUE : SRB_FUNCTION_UNLOCK_QUEUE; srb->OriginalRequest = irp; srb->DataBuffer = NULL; srb->QueueTag = SP_UNTAGGED; if(BypassLockedQueue) { srb->SrbFlags |= SRB_FLAGS_BYPASS_LOCKED_QUEUE; } IoSetCompletionRoutine(irp, SpSignalCompletion, event, TRUE, TRUE, TRUE); irpStack = IoGetNextIrpStackLocation(irp); irpStack->Parameters.Scsi.Srb = srb; irpStack->MajorFunction = IRP_MJ_SCSI; status = IoCallDriver(LogicalUnit, irp); if(status == STATUS_PENDING) { KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL); status = irp->IoStatus.Status; } } finally { if(irp != NULL) { IoFreeIrp(irp); } if(srb != NULL) { ExFreePool(srb); } if(event != NULL) { ExFreePool(event); } } return status; } NTSTATUS SpCheckSpecialDeviceFlags( IN PLOGICAL_UNIT_EXTENSION LogicalUnit, IN PINQUIRYDATA InquiryData ) /*++ Routine Description: This routine will check the registry to determine what sort of special handling a target device requires. If there is a device-node then the routine will check under the device parameters for this particular logical unit. If there is no device node for the logical unit the hardware id will be looked up (first) under the devnode for the adapter or (if not found) the bad controller list stored under the scsiport Control key. The flags tested for include (this list should be updated as more are added): * OneLun - used to keep from enumerating past LUN 0 on a particular device. * SparseLun - used to indicate the device may have holes in the LUN numbers. * NonStandardVPD - used to indicate that a target does not support VPD 0x00 but does support VPD 0x80 and 0x83. * BinarySN - used to indicate that the target supplied a binary serial number and that we need to convert it to ascii. These values are REG_DWORD's. REG_NULL can be used to return a value to it's default. Arguments: LogicalUnit - the logical unit InquiryData - the inquiry data retreived for the lun Return Value: status --*/ { HANDLE baseKey = NULL; HANDLE listKey = NULL; HANDLE entryKey = NULL; UNICODE_STRING keyName; OBJECT_ATTRIBUTES objectAttributes; SP_SPECIAL_CONTROLLER_FLAGS flags = { 0, // SparseLun 0, // OneLun 0, // LargeLuns 0, // SetLunInCdb 0, // NonStandardVPD 0 // BinarySN }; NTSTATUS status; PAGED_CODE(); DebugPrint((1, "SpCheckSpecialDeviceFlags - checking flags for %#p\n", LogicalUnit)); // // Check the bad controller list in the scsiport control key // try { DebugPrint((2, "SpCheckSpecialDeviceFlags - trying control list\n")); RtlInitUnicodeString(&keyName, SCSIPORT_CONTROL_KEY SCSIPORT_SPECIAL_TARGET_KEY); InitializeObjectAttributes(&objectAttributes, &keyName, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&listKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening " "key %wZ\n", status, &keyName)); leave; } SpProcessSpecialControllerList( LogicalUnit->DeviceObject->DriverObject, InquiryData, listKey, &flags); } finally { if(listKey != NULL) { ZwClose(listKey); listKey = NULL; } } // // Next check the special list in the adapter's devnode. // try { PDEVICE_OBJECT adapterPdo = LogicalUnit->AdapterExtension->LowerPdo; DebugPrint((2, "SpCheckSpecialDeviceFlags - trying adapter list\n")); status = IoOpenDeviceRegistryKey(adapterPdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &baseKey); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening " "adapter devnode key\n", status)); leave; } RtlInitUnicodeString(&keyName, L"ScsiPort\\" SCSIPORT_SPECIAL_TARGET_KEY); InitializeObjectAttributes(&objectAttributes, &keyName, OBJ_CASE_INSENSITIVE, baseKey, NULL); status = ZwOpenKey(&listKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening " "adapter devnode key %wZ\n", status, &keyName)); leave; } SpProcessSpecialControllerList( LogicalUnit->DeviceObject->DriverObject, InquiryData, listKey, &flags); } finally { if(baseKey != NULL) { ZwClose(baseKey); baseKey = NULL; if(listKey != NULL) { ZwClose(listKey); listKey = NULL; } } } // // Finally check the devnode (if any) for the logical unit. This one is // special - the hardware id already matchs so the key just contains the // values to be used, not a database of values. // try { status = IoOpenDeviceRegistryKey(LogicalUnit->CommonExtension.DeviceObject, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &baseKey); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening " "device devnode key\n", status)); leave; } RtlInitUnicodeString(&keyName, L"ScsiPort\\" SCSIPORT_SPECIAL_TARGET_KEY); InitializeObjectAttributes(&objectAttributes, &keyName, OBJ_CASE_INSENSITIVE, baseKey, NULL); status = ZwOpenKey(&listKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening " "device devnode key %wZ\n", status, &keyName)); leave; } SpProcessSpecialControllerFlags(listKey, &flags); } finally { if(baseKey != NULL) { ZwClose(baseKey); baseKey = NULL; if(listKey != NULL) { ZwClose(listKey); listKey = NULL; } } } LogicalUnit->SpecialFlags = flags; return STATUS_SUCCESS; } VOID SpProcessSpecialControllerList( IN PDRIVER_OBJECT DriverObject, IN PINQUIRYDATA InquiryData, IN HANDLE ListKey, OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags ) /*++ Routine Description: This routine will match the specified logical unit to a set of special controller flags stored in the registry key ListKey. These flags will be written into the Flags structure, overwriting any flags which already exist. If no logical unit is provided then the ListKey handle is assumed to point at the appropriate list entry and the values stored there will be copied into the Flags structure. Arguments: InquiryData - The inquiry data for the logical unit. This is used to match strings in the special target list. ListKey - a handle to the special controller list to locate the logical unit in, or a handle to a list of flags if the LogicalUnit value is not present. Flags - a location to store the flags. Return Value: None --*/ { UNICODE_STRING hardwareIds; PWSTR *hardwareIdList; ULONG idNumber; NTSTATUS status; PAGED_CODE(); RtlInitUnicodeString(&hardwareIds, NULL); status = ScsiPortGetHardwareIds(DriverObject, InquiryData, &hardwareIds); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpProcessSpecialControllerList: Error %#08lx getting " "hardware id's\n", status)); return; } status = SpMultiStringToStringArray(DriverObject, &hardwareIds, &hardwareIdList, FALSE); if(!NT_SUCCESS(status)) { RtlFreeUnicodeString(&hardwareIds); return; } for(idNumber = 0; hardwareIdList[idNumber] != NULL; idNumber++) { PWSTR hardwareId = hardwareIdList[idNumber]; ULONG j; UNICODE_STRING keyName; OBJECT_ATTRIBUTES objectAttributes; HANDLE flagsKey; DebugPrint((2, "SpProcessSpecialControllerList: processing id %ws\n", hardwareId)); // // Remove the leading slash from the name. // for(j = 0; hardwareId[j] != UNICODE_NULL; j++) { if(hardwareId[j] == L'\\') { hardwareId = &(hardwareId[j+1]); break; } } // // Process the hardware id that we just found the end of. // RtlInitUnicodeString(&keyName, hardwareId); DebugPrint((2, "SpProcessSpecialControllerList: Finding match for " "%wZ - id %d\n", &keyName, idNumber)); InitializeObjectAttributes(&objectAttributes, &keyName, OBJ_CASE_INSENSITIVE, ListKey, NULL); status = ZwOpenKey(&flagsKey, KEY_READ, &objectAttributes); if(NT_SUCCESS(status)) { SpProcessSpecialControllerFlags(flagsKey, Flags); ZwClose(flagsKey); } else { DebugPrint((2, "SpProcessSpecialControllerList: Error %#08lx " "opening key\n", status)); } } ExFreePool(hardwareIdList); RtlFreeUnicodeString(&hardwareIds); return; } VOID SpProcessSpecialControllerFlags( IN HANDLE FlagsKey, OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags ) { RTL_QUERY_REGISTRY_TABLE queryTable[7]; NTSTATUS status; PAGED_CODE(); RtlZeroMemory(queryTable, sizeof(queryTable)); queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[0].Name = L"SparseLUN"; queryTable[0].EntryContext = &(Flags->SparseLun); queryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[1].Name = L"OneLUN"; queryTable[1].EntryContext = &(Flags->OneLun); queryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[2].Name = L"LargeLuns"; queryTable[2].EntryContext = &(Flags->LargeLuns); queryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[3].Name = L"SetLunInCdb"; queryTable[3].EntryContext = &(Flags->SetLunInCdb); queryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[4].Name = L"NonStandardVPD"; queryTable[4].EntryContext = &(Flags->NonStandardVPD); queryTable[5].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[5].Name = L"BinarySN"; queryTable[5].EntryContext = &(Flags->BinarySN); status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, FlagsKey, queryTable, NULL, NULL); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpProcssSpecialControllerFlags: Error %#08lx reading " "values\n", status)); } else { DebugPrint((2, "SpProcessSpecialControllerFlags: %s%s%s%s%s\n", ((Flags->SparseLun || Flags->OneLun || Flags->LargeLuns || Flags->SetLunInCdb || Flags->NonStandardVPD || Flags->BinarySN) ? "" : "none"), (Flags->SparseLun ? "SparseLun " : ""), (Flags->OneLun ? "OneLun " : ""), (Flags->LargeLuns ? "LargeLuns " : ""), (Flags->SetLunInCdb ? "SetLunInCdb" : ""), (Flags->NonStandardVPD ? "NonStandardVPD" : ""), (Flags->BinarySN ? "BinarySN" : ""))); } return; } PSRB_DATA FASTCALL SpAllocateSrbData( IN PADAPTER_EXTENSION Adapter, IN OPTIONAL PIRP Request ) { PSRB_DATA srbData; srbData = ExAllocateFromNPagedLookasideList( &Adapter->SrbDataLookasideList); #if TEST_LISTS if(srbData != NULL) { InterlockedIncrement64(&Adapter->SrbDataAllocationCount); } #endif if((srbData == NULL) && (Request != NULL)) { KIRQL oldIrql; PSRB_DATA emergencySrbData; // // Use the emergency srb data if it's not already in use. // KeAcquireSpinLock(&Adapter->EmergencySrbDataSpinLock, &oldIrql); emergencySrbData = (PSRB_DATA) InterlockedExchangePointer( (PVOID) &(Adapter->EmergencySrbData), NULL); if(emergencySrbData == NULL) { // // It's in use - queue the request until an srb data block // goes free. // InsertTailList( &Adapter->SrbDataBlockedRequests, &Request->Tail.Overlay.DeviceQueueEntry.DeviceListEntry); } else { // // There is an SRB_DATA block available after all. // srbData = emergencySrbData; #if TEST_LISTS InterlockedIncrement64(&Adapter->SrbDataEmergencyFreeCount); #endif } KeReleaseSpinLock(&Adapter->EmergencySrbDataSpinLock, oldIrql); } return srbData; } VOID FASTCALL SpFreeSrbData( IN PADAPTER_EXTENSION Adapter, IN PSRB_DATA SrbData ) { PSRB_DATA emergencySrbData = NULL; BOOLEAN startedRequest = FALSE; LONG depth; ASSERT_SRB_DATA(SrbData); ASSERT(SrbData->CurrentIrp == NULL); ASSERT(SrbData->CurrentSrb == NULL); ASSERT(SrbData->CompletedRequests == NULL); // // Determine if there are any other instances of this routine running. If // there are, don't start a blocked request. // depth = InterlockedIncrement(&Adapter->SrbDataFreeRunning); // // Clear out some of the flags so we don't get confused when we reuse this // request. // SrbData->Flags = 0; // // See if we need to store away a new emergency SRB_DATA block // emergencySrbData = InterlockedCompareExchangePointer( &(Adapter->EmergencySrbData), SrbData, NULL); // // If we stored this SRB_DATA block as the new emergency block AND if this // routine is not recursively nested, check if there are any blocked // requests waiting to be started. // if(emergencySrbData == NULL && depth == 1) { KIRQL oldIrql; CheckForBlockedRequest: // // We did - now grab the spinlock and see if we can use it to issue // a new request. // KeAcquireSpinLock(&(Adapter->EmergencySrbDataSpinLock), &oldIrql); // // First check to see if we have a request to process. // if(IsListEmpty(&(Adapter->SrbDataBlockedRequests)) == TRUE) { KeReleaseSpinLock(&(Adapter->EmergencySrbDataSpinLock), oldIrql); InterlockedDecrement(&Adapter->SrbDataFreeRunning); return; } // // make sure the emergency request is still there (doesn't really // matter if it's the one we were called with or another one - just // make sure one is available). // emergencySrbData = (PSRB_DATA) InterlockedExchangePointer( (PVOID) &(Adapter->EmergencySrbData), NULL); if(emergencySrbData == NULL) { // // Our work here is done. // KeReleaseSpinLock(&(Adapter->EmergencySrbDataSpinLock), oldIrql); InterlockedDecrement(&Adapter->SrbDataFreeRunning); return; } else { PLIST_ENTRY entry; PIRP request; PIO_STACK_LOCATION currentIrpStack; PSCSI_REQUEST_BLOCK srb; entry = RemoveHeadList(&(Adapter->SrbDataBlockedRequests)); ASSERT(entry != NULL); request = CONTAINING_RECORD( entry, IRP, Tail.Overlay.DeviceQueueEntry); KeReleaseSpinLock(&(Adapter->EmergencySrbDataSpinLock), oldIrql); currentIrpStack = IoGetCurrentIrpStackLocation(request); srb = currentIrpStack->Parameters.Scsi.Srb; ASSERT_PDO(currentIrpStack->DeviceObject); emergencySrbData->CurrentIrp = request; emergencySrbData->CurrentSrb = srb; emergencySrbData->LogicalUnit = currentIrpStack->DeviceObject->DeviceExtension; srb->OriginalRequest = emergencySrbData; startedRequest = TRUE; SpDispatchRequest(emergencySrbData->LogicalUnit, request); #if TEST_LISTS InterlockedIncrement64(&Adapter->SrbDataResurrectionCount); #endif } // // If we started a blocked request, go back and see if another one // needs to be started. // if (startedRequest == TRUE) { startedRequest = FALSE; goto CheckForBlockedRequest; } } else if (emergencySrbData != NULL) { // // We did not store this SRB_DATA block as the emergency block, so // we need to free it back to the lookaside list. // ExFreeToNPagedLookasideList( &Adapter->SrbDataLookasideList, SrbData); } InterlockedDecrement(&Adapter->SrbDataFreeRunning); return; } PVOID SpAllocateSrbDataBackend( IN POOL_TYPE PoolType, IN ULONG NumberOfBytes, IN ULONG AdapterIndex ) { KIRQL oldIrql; PADAPTER_EXTENSION Adapter; PSRB_DATA srbData; ULONG tag; KeAcquireSpinLock(&ScsiGlobalAdapterListSpinLock, &oldIrql); Adapter = ScsiGlobalAdapterList[AdapterIndex]->DeviceExtension; KeReleaseSpinLock(&ScsiGlobalAdapterListSpinLock, oldIrql); ASSERT_FDO(Adapter->DeviceObject); tag = SpAllocateQueueTag(Adapter); if(tag == -1) { return NULL; } srbData = SpAllocatePool(PoolType, NumberOfBytes, SCSIPORT_TAG_SRB_DATA, Adapter->DeviceObject->DriverObject); if(srbData == NULL) { SpReleaseQueueTag(Adapter, tag); return NULL; } RtlZeroMemory(srbData, sizeof(SRB_DATA)); srbData->Adapter = Adapter; srbData->QueueTag = tag; srbData->Type = SRB_DATA_TYPE; srbData->Size = sizeof(SRB_DATA); srbData->Flags = 0; srbData->FreeRoutine = SpFreeSrbData; return srbData; } VOID SpFreeSrbDataBackend( IN PSRB_DATA SrbData ) { ASSERT_SRB_DATA(SrbData); ASSERT_FDO(SrbData->Adapter->DeviceObject); ASSERT(SrbData->QueueTag != 0); SpReleaseQueueTag(SrbData->Adapter, SrbData->QueueTag); SrbData->Type = 0; ExFreePool(SrbData); return; } NTSTATUS SpAllocateTagBitMap( IN PADAPTER_EXTENSION Adapter ) { ULONG size; // number of bits PRTL_BITMAP bitMap; PAGED_CODE(); // // Initialize the queue tag bitMap. // if(Adapter->MaxQueueTag == 0) { #if SMALL_QUEUE_TAG_BITMAP if(Adapter->NumberOfRequests <= 240) { Adapter->MaxQueueTag = (UCHAR) (Adapter->NumberOfRequests) + 10; } else { Adapter->MaxQueueTag = 254; } #else Adapter->MaxQueueTag = 254; #endif } else if (Adapter->MaxQueueTag < Adapter->NumberOfRequests) { DbgPrint("SpAllocateTagBitmap: MaxQueueTag %d < NumberOfRequests %d\n" "This will negate the advantage of having increased the " "number of requests.\n", Adapter->MaxQueueTag, Adapter->NumberOfRequests); } DebugPrint((1, "SpAllocateAdapterResources: %d bits in queue tag " "bitMap\n", Adapter->MaxQueueTag)); size = (Adapter->MaxQueueTag + 1); size /= 8; size += 1; size *= sizeof(UCHAR); size += sizeof(RTL_BITMAP); bitMap = SpAllocatePool(NonPagedPool, size, SCSIPORT_TAG_QUEUE_BITMAP, Adapter->DeviceObject->DriverObject); if(bitMap == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlInitializeBitMap(bitMap, (PULONG) (bitMap + 1), Adapter->MaxQueueTag); RtlClearAllBits(bitMap); // // Queue tag 0 is invalid and should never be returned by the allocator. // RtlSetBits(bitMap, 0, 1); Adapter->QueueTagBitMap = bitMap; Adapter->QueueTagHint = 1; // // Create a spinlock to protect our queue tag bitmap. There's no reason // for this to contend with the regular port spinlock. // KeInitializeSpinLock(&(Adapter->QueueTagSpinLock)); return STATUS_SUCCESS; } ULONG SpAllocateQueueTag( IN PADAPTER_EXTENSION Adapter ) { KIRQL oldIrql; ULONG tagValue; ASSERT_FDO(Adapter->DeviceObject); KeAcquireSpinLock(&(Adapter->QueueTagSpinLock), &oldIrql); // // Find an available queue tag. // tagValue = RtlFindClearBitsAndSet(Adapter->QueueTagBitMap, 1, Adapter->QueueTagHint); KeReleaseSpinLock(&(Adapter->QueueTagSpinLock), oldIrql); ASSERT(Adapter->QueueTagHint < Adapter->MaxQueueTag); ASSERT(tagValue != 0); if(tagValue != -1) { ASSERT(tagValue <= Adapter->MaxQueueTag); // // This we can do unsynchronized. if we nuke the hint accidentally it // will just increase the cost of the next lookup which should // hopefully occur rarely. // Adapter->QueueTagHint = (tagValue + 1) % Adapter->MaxQueueTag; } return tagValue; } VOID SpReleaseQueueTag( IN PADAPTER_EXTENSION Adapter, IN ULONG QueueTag ) { KIRQL oldIrql; KeAcquireSpinLock(&(Adapter->QueueTagSpinLock), &oldIrql); RtlClearBits(Adapter->QueueTagBitMap, QueueTag, 1); KeReleaseSpinLock(&(Adapter->QueueTagSpinLock), oldIrql); return; } INTERFACE_TYPE SpGetPdoInterfaceType( IN PDEVICE_OBJECT Pdo ) { ULONG value; GUID busTypeGuid; INTERFACE_TYPE interfaceType = InterfaceTypeUndefined; ULONG result; NTSTATUS status; PAGED_CODE(); status = SpReadNumericInstanceValue(Pdo, L"LegacyInterfaceType", &value); if(NT_SUCCESS(status)) { interfaceType = value; return interfaceType; } // // Attempt to get and interpret the bus type GUID. // status = IoGetDeviceProperty(Pdo, DevicePropertyBusTypeGuid, sizeof(GUID), &busTypeGuid, &result); if(NT_SUCCESS(status)) { ULONG i; for(i = 0; (SpGuidInterfaceMappingList[i].InterfaceType != InterfaceTypeUndefined); i++) { if(RtlEqualMemory(&(SpGuidInterfaceMappingList[i].Guid), &busTypeGuid, sizeof(GUID))) { // // We have a legacy interface type for this guid already. // interfaceType = SpGuidInterfaceMappingList[i].InterfaceType; break; } } } if(interfaceType != InterfaceTypeUndefined) { return interfaceType; } status = IoGetDeviceProperty(Pdo, DevicePropertyLegacyBusType, sizeof(INTERFACE_TYPE), &interfaceType, &result); if(NT_SUCCESS(status)) { ASSERT(result == sizeof(INTERFACE_TYPE)); // // Munge the interface type for the case of PCMCIA cards to allow SCSI // pccards (i.e. sparrow) to be recognized. Much better would be a way // to get the interface type correct before we enter this routine. // if (interfaceType == PCMCIABus) { interfaceType = Isa; } } if(interfaceType != InterfaceTypeUndefined) { return interfaceType; } else { // // No idea what the interface type is - guess isa. // DebugPrint((1, "SpGetPdoInterfaceType: Status %#08lx getting legacy " "bus type - assuming device is ISA\n", status)); return Isa; } } NTSTATUS SpReadNumericInstanceValue( IN PDEVICE_OBJECT Pdo, IN PWSTR ValueName, OUT PULONG Value ) { ULONG value; HANDLE baseKey = NULL; HANDLE scsiportKey = NULL; NTSTATUS status; PAGED_CODE(); ASSERT(Value != NULL); ASSERT(ValueName != NULL); ASSERT(Pdo != NULL); status = IoOpenDeviceRegistryKey(Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &baseKey); if(!NT_SUCCESS(status)) { return status; } try { UNICODE_STRING unicodeKeyName; OBJECT_ATTRIBUTES objectAttributes; RtlInitUnicodeString(&unicodeKeyName, L"Scsiport"); InitializeObjectAttributes(&objectAttributes, &unicodeKeyName, OBJ_CASE_INSENSITIVE, baseKey, NULL); status = ZwOpenKey(&scsiportKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { leave; } else { UNICODE_STRING unicodeValueName; UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION keyValue = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; ULONG result; RtlInitUnicodeString(&unicodeValueName, ValueName); status = ZwQueryValueKey(scsiportKey, &unicodeValueName, KeyValuePartialInformation, keyValue, sizeof(buffer), &result); if(!NT_SUCCESS(status)) { leave; } if(keyValue->Type != REG_DWORD) { status = STATUS_OBJECT_TYPE_MISMATCH; leave; } if(result < sizeof(ULONG)) { status = STATUS_OBJECT_TYPE_MISMATCH; leave; } value = ((PULONG) (keyValue->Data))[0]; } } finally { if(baseKey != NULL) {ZwClose(baseKey);} if(scsiportKey != NULL) {ZwClose(scsiportKey);} } *Value = value; return status; } NTSTATUS SpWriteNumericInstanceValue( IN PDEVICE_OBJECT Pdo, IN PWSTR ValueName, IN ULONG Value ) { ULONG value; HANDLE baseKey = NULL; HANDLE scsiportKey = NULL; NTSTATUS status; PAGED_CODE(); ASSERT(ValueName != NULL); ASSERT(Pdo != NULL); status = IoOpenDeviceRegistryKey(Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ | KEY_WRITE, &baseKey); if(!NT_SUCCESS(status)) { return status; } try { UNICODE_STRING unicodeKeyName; OBJECT_ATTRIBUTES objectAttributes; RtlInitUnicodeString(&unicodeKeyName, L"Scsiport"); InitializeObjectAttributes(&objectAttributes, &unicodeKeyName, OBJ_CASE_INSENSITIVE, baseKey, NULL); status = ZwCreateKey(&scsiportKey, KEY_READ | KEY_WRITE, &objectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL ); if(!NT_SUCCESS(status)) { leave; } else { UNICODE_STRING unicodeValueName; ULONG result; RtlInitUnicodeString(&unicodeValueName, ValueName); status = ZwSetValueKey(scsiportKey, &unicodeValueName, 0, REG_DWORD, &Value, sizeof(ULONG)); } } finally { if(baseKey != NULL) {ZwClose(baseKey);} if(scsiportKey != NULL) {ZwClose(scsiportKey);} } return status; } PMAPPED_ADDRESS SpAllocateAddressMapping( PADAPTER_EXTENSION Adapter ) /*++ Routine Description: This routine will attempt to allocate a free address mapping block and place it on the adapter's MappedAddressList. If there is an available block in the free list then it will be used. Otherwise it will attempt to allocate a block from non-paged pool. Arguments: Adapter - the adapter we are allocating the mapping for. Preallocate - indicates that the caller is trying to preallocate buffers. Return Value: a pointer to the new mapping (which has been inserted into the address mapping list) or NULL if none could be allocated. --*/ { PMAPPED_ADDRESS mapping; PAGED_CODE(); // // First check the free address mapping list. If there's one there // unlink it and return. // if(Adapter->FreeMappedAddressList != NULL) { mapping = Adapter->FreeMappedAddressList; Adapter->FreeMappedAddressList = mapping->NextMappedAddress; } else { mapping = SpAllocatePool(NonPagedPool, sizeof(MAPPED_ADDRESS), SCSIPORT_TAG_MAPPING_LIST, Adapter->DeviceObject->DriverObject); } if(mapping == NULL) { DebugPrint((0, "SpAllocateAddressMapping: Unable to allocate " "mapping\n")); return NULL; } RtlZeroMemory(mapping, sizeof(MAPPED_ADDRESS)); mapping->NextMappedAddress = Adapter->MappedAddressList; Adapter->MappedAddressList = mapping; return mapping; } BOOLEAN SpPreallocateAddressMapping( PADAPTER_EXTENSION Adapter, IN UCHAR NumberOfBlocks ) /*++ Routine Description: This routine will allocate a number of address mapping structures and place them on the free mapped address list. Arguments: Adapter - the adapter we are allocating the mapping for. NumberOfBlocks - the number of blocks to allocate Return Value: TRUE if the requested number of blocks was successfully allocated, FALSE if there was not sufficient memory to allocate them all. The caller is still responsible for freeing them in this case. --*/ { PMAPPED_ADDRESS mapping; ULONG i; PAGED_CODE(); for(i = 0; i < NumberOfBlocks; i++) { mapping = SpAllocatePool(NonPagedPool, sizeof(MAPPED_ADDRESS), SCSIPORT_TAG_MAPPING_LIST, Adapter->DeviceObject->DriverObject); if(mapping == NULL) { return FALSE; } RtlZeroMemory(mapping, sizeof(MAPPED_ADDRESS)); mapping->NextMappedAddress = Adapter->FreeMappedAddressList; Adapter->FreeMappedAddressList = mapping; } return TRUE; } VOID SpPurgeFreeMappedAddressList( IN PADAPTER_EXTENSION Adapter ) /*++ Routine Description: This routine frees all of the mapped address blocks on the FreeMappedAddressList. Arguments: Adapter - the adapter who's FreeMappedAddressList is to be dumped. Return Value: none --*/ { PMAPPED_ADDRESS mapping; PAGED_CODE(); while(Adapter->FreeMappedAddressList != NULL) { mapping = Adapter->FreeMappedAddressList; Adapter->FreeMappedAddressList = mapping->NextMappedAddress; ExFreePool(mapping); } return; } BOOLEAN SpFreeMappedAddress( IN PADAPTER_EXTENSION Adapter, IN PVOID MappedAddress ) /*++ Routine Description: This routine will unmap the specified mapping and then return the mapping block to the free list. If no mapped address was specified then this will simply free the first mapping on the MappedAddressList. Arguments: Adapter - the adapter which has the mapping MappedAddress - the base address of the mapping we're attempting to free. ignored if FreeSpecificBlock is false. Return Value: TRUE if a matching list element was found. FALSE otherwise. --*/ { PMAPPED_ADDRESS *mapping; PAGED_CODE(); for(mapping = &(Adapter->MappedAddressList); *mapping != NULL; mapping = &((*mapping)->NextMappedAddress)) { if((*mapping)->MappedAddress == MappedAddress) { PMAPPED_ADDRESS tmp = *mapping; // // Unmap address. // MmUnmapIoSpace(tmp->MappedAddress, tmp->NumberOfBytes); // // Unlink this entry from the mapped address list. Stick it on // the free mapped address list. Then return. // *mapping = tmp->NextMappedAddress; tmp->NextMappedAddress = Adapter->FreeMappedAddressList; Adapter->FreeMappedAddressList = tmp; return TRUE; } } return FALSE; } PMAPPED_ADDRESS SpFindMappedAddress( IN PADAPTER_EXTENSION Adapter, IN LARGE_INTEGER IoAddress, IN ULONG NumberOfBytes, IN ULONG SystemIoBusNumber ) { PMAPPED_ADDRESS mapping; for(mapping = Adapter->MappedAddressList; mapping != NULL; mapping = mapping->NextMappedAddress) { if((mapping->IoAddress.QuadPart == IoAddress.QuadPart) && (mapping->NumberOfBytes == NumberOfBytes) && (mapping->BusNumber == SystemIoBusNumber)) { return mapping; } } return NULL; } VOID SpReleaseMappedAddresses( IN PADAPTER_EXTENSION Adapter ) { ULONG i; PAGED_CODE(); // // Iterate through the mapped address list and punt every entry onto the // free list. // while(Adapter->MappedAddressList != NULL) { SpFreeMappedAddress(Adapter, Adapter->MappedAddressList->MappedAddress); } // // Now dump the free list. // SpPurgeFreeMappedAddressList(Adapter); return; } NTSTATUS SpSignalCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT Event ) { KeSetEvent(Event, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS SpSendIrpSynchronous( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KEVENT event; PAGED_CODE(); KeInitializeEvent(&event, SynchronizationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, SpSignalCompletion, &event, TRUE, TRUE, TRUE); IoCallDriver(DeviceObject, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); return Irp->IoStatus.Status; } NTSTATUS SpGetBusTypeGuid( IN PADAPTER_EXTENSION Adapter ) { ULONG result; NTSTATUS status; PAGED_CODE(); // // Grab the bus interface GUID and save it away in the adapter extension. // status = IoGetDeviceProperty(Adapter->LowerPdo, DevicePropertyBusTypeGuid, sizeof(GUID), &(Adapter->BusTypeGuid), &result); if(!NT_SUCCESS(status)) { RtlZeroMemory(&(Adapter->BusTypeGuid), sizeof(GUID)); } return status; } BOOLEAN SpDetermine64BitSupport( VOID ) /*++ Routine Description: This routine determines if 64-bit physical addressing is supported by the system to be saved in the global Sp64BitPhysicalAddressing. Eventually this routine can be removed and the scsiport global will just point to the one exported by MM. However the global isn't hooked up for PAE36 at the moment so we need to do some x86 specific tricks. Arguments: none Return Value: Does the system support 64-bit (or something over 32-bit) addresses? --*/ { PAGED_CODE(); if((*Mm64BitPhysicalAddress) == TRUE) { DbgPrintEx(DPFLTR_SCSIPORT_ID, DPFLTR_INFO_LEVEL, "SpDetermine64BitSupport: Mm64BitPhysicalAddress is TRUE\n"); return TRUE; } return FALSE; } VOID SpAdjustDisabledBit( IN PLOGICAL_UNIT_EXTENSION LogicalUnit, IN BOOLEAN Enable ) { ULONG newCount; KIRQL oldIrql; KeAcquireSpinLock(&(LogicalUnit->AdapterExtension->SpinLock), &oldIrql); if(Enable) { if(LogicalUnit->QueuePauseCount != 0) { LogicalUnit->QueuePauseCount -= 1; } if(LogicalUnit->QueuePauseCount == 0) { CLEAR_FLAG(LogicalUnit->LuFlags, LU_QUEUE_PAUSED); } } else { LogicalUnit->QueuePauseCount += 1; SET_FLAG(LogicalUnit->LuFlags, LU_QUEUE_PAUSED); } KeReleaseSpinLock(&(LogicalUnit->AdapterExtension->SpinLock), oldIrql); return; } NTSTATUS SpReadNumericValue( IN OPTIONAL HANDLE Root, IN OPTIONAL PUNICODE_STRING KeyName, IN PUNICODE_STRING ValueName, OUT PULONG Value ) /*++ Routine Description: This routine will read a REG_DWORD value from the specified registry location. The caller can specify the key by providing a handle to a root registry key and the name of a subkey. The caller must supply either Root or KeyName. Both may be supplied. Arguments: Root - the key the value resides in (if KeyName is NULL), a parent key of the one the value resides in, or NULL if KeyName specifies the entire registry path. KeyName - the name of the subkey (either from the root of the registry or from the key specified in Root. ValueName - the name of the value to be read Value - returns the value in the key. this will be zero if an error occurs Return Value: STATUS_SUCCESS if successful. STATUS_UNSUCCESSFUL if the specified value is not a REG_DWORD value. other status values explaining the cause of the failure. --*/ { ULONG value = 0; HANDLE key = Root; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); ASSERT(Value != NULL); ASSERT(ValueName != NULL); ASSERT((KeyName != NULL) || (Root != NULL)); if(ARGUMENT_PRESENT(KeyName)) { OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&(objectAttributes), KeyName, OBJ_CASE_INSENSITIVE | OBJ_OPENIF, Root, NULL); status = ZwOpenKey(&(key), KEY_QUERY_VALUE, &objectAttributes); } if(NT_SUCCESS(status)) { UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION data; ULONG result; RtlZeroMemory(buffer, sizeof(buffer)); data = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; status = ZwQueryValueKey(key, ValueName, KeyValuePartialInformation, data, sizeof(buffer), &result); if(NT_SUCCESS(status)) { if (data->Type != REG_DWORD) { status = STATUS_UNSUCCESSFUL; } else { value = ((PULONG) data->Data)[0]; } } } *Value = value; if(key != Root) { ZwClose(key); } return status; } PMDL SpBuildMdlForMappedTransfer( IN PDEVICE_OBJECT DeviceObject, IN PDMA_ADAPTER AdapterObject, IN PMDL OriginalMdl, IN PVOID StartVa, IN ULONG ByteCount, IN PSRB_SCATTER_GATHER ScatterGatherList, IN ULONG ScatterGatherEntries ) { ULONG size; PMDL mdl; ULONG pageCount; PPFN_NUMBER pages; ULONG sgPage; ULONG mdlPage; ULONG sgSpan; mdl = SpAllocateMdl(StartVa, ByteCount, FALSE, FALSE, NULL, DeviceObject->DriverObject); if (mdl == NULL) { return NULL; } pageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(StartVa, ByteCount); // // Indicate that the memory has already been locked down. // // // Indicate that the memory is "I/O space" so that MM doesn't won't // reference the (nonexistent) PFNs for this buffer. We have to do this // for the time being because MM isn't aware of the pages the HAL is using // for bounce buffers. // SET_FLAG(mdl->MdlFlags, MDL_PAGES_LOCKED | MDL_IO_SPACE); // // Run through our scatter gather list and build the page list based // on that. // pages = (PPFN_NUMBER) (mdl + 1); for(sgPage = 0, mdlPage = 0; sgPage < ScatterGatherEntries; sgPage++) { PVOID pa; ULONG sgLength; ASSERT(ScatterGatherList[sgPage].Length != 0); pa = (PVOID) ScatterGatherList[sgPage].Address.QuadPart; sgLength = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pa, ScatterGatherList[sgPage].Length); for(sgSpan = 0; sgSpan < sgLength; sgSpan++, mdlPage++) { ULONGLONG pa; pa = ScatterGatherList[sgPage].Address.QuadPart; pa += sgSpan * PAGE_SIZE; pa >>= PAGE_SHIFT; pages[mdlPage] = (PFN_NUMBER) (pa); } } pages = (PPFN_NUMBER) (mdl + 1); pages = (PPFN_NUMBER) (OriginalMdl + 1); ASSERT(mdlPage == pageCount); return mdl; } #if defined(FORWARD_PROGRESS) VOID SpPrepareMdlForMappedTransfer( IN PMDL mdl, IN PDEVICE_OBJECT DeviceObject, IN PDMA_ADAPTER AdapterObject, IN PMDL OriginalMdl, IN PVOID StartVa, IN ULONG ByteCount, IN PSRB_SCATTER_GATHER ScatterGatherList, IN ULONG ScatterGatherEntries ) { ULONG size; ULONG pageCount; PPFN_NUMBER pages; ULONG sgPage; ULONG mdlPage; ULONG sgSpan; pageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(StartVa, ByteCount); // // Indicate that the memory has already been locked down. // // // Indicate that the memory is "I/O space" so that MM doesn't won't // reference the (nonexistent) PFNs for this buffer. We have to do this // for the time being because MM isn't aware of the pages the HAL is using // for bounce buffers. // SET_FLAG(mdl->MdlFlags, MDL_PAGES_LOCKED | MDL_IO_SPACE); // // Run through our scatter gather list and build the page list based // on that. // pages = (PPFN_NUMBER) (mdl + 1); for(sgPage = 0, mdlPage = 0; sgPage < ScatterGatherEntries; sgPage++) { PVOID pa; ULONG sgLength; ASSERT(ScatterGatherList[sgPage].Length != 0); pa = (PVOID) ScatterGatherList[sgPage].Address.QuadPart; sgLength = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pa, ScatterGatherList[sgPage].Length); for(sgSpan = 0; sgSpan < sgLength; sgSpan++, mdlPage++) { ULONGLONG pa; pa = ScatterGatherList[sgPage].Address.QuadPart; pa += sgSpan * PAGE_SIZE; pa >>= PAGE_SHIFT; pages[mdlPage] = (PFN_NUMBER) (pa); } } pages = (PPFN_NUMBER) (mdl + 1); pages = (PPFN_NUMBER) (OriginalMdl + 1); ASSERT(mdlPage == pageCount); } #endif PSRB_DATA FASTCALL SpAllocateBypassSrbData( IN PLOGICAL_UNIT_EXTENSION LogicalUnit ) { PSINGLE_LIST_ENTRY entry; PSRB_DATA srbData; entry = ExInterlockedPopEntrySList(&(LogicalUnit->BypassSrbDataList), &(LogicalUnit->BypassSrbDataSpinLock)); if(entry == NULL) { KeBugCheckEx(PORT_DRIVER_INTERNAL, 5, NUMBER_BYPASS_SRB_DATA_BLOCKS, (ULONG_PTR) LogicalUnit->BypassSrbDataBlocks, 0); } srbData = CONTAINING_RECORD(entry, SRB_DATA, Reserved); srbData->Adapter = LogicalUnit->AdapterExtension; srbData->QueueTag = SP_UNTAGGED; srbData->Type = SRB_DATA_TYPE; srbData->Size = sizeof(SRB_DATA); srbData->Flags = SRB_DATA_BYPASS_REQUEST; srbData->FreeRoutine = SpFreeBypassSrbData; return srbData; } VOID FASTCALL SpFreeBypassSrbData( IN PADAPTER_EXTENSION Adapter, IN PSRB_DATA SrbData ) { PLOGICAL_UNIT_EXTENSION lu = SrbData->LogicalUnit; ASSERT_SRB_DATA(SrbData); ASSERT(SrbData->CurrentIrp == NULL); ASSERT(SrbData->CurrentSrb == NULL); ASSERT(SrbData->CompletedRequests == NULL); ASSERT(TEST_FLAG(SrbData->Flags, SRB_DATA_BYPASS_REQUEST)); RtlZeroMemory(SrbData, sizeof(SRB_DATA)); ExInterlockedPushEntrySList(&(lu->BypassSrbDataList), &(SrbData->Reserved), &(lu->BypassSrbDataSpinLock)); return; } PVOID SpAllocateErrorLogEntry( IN PDRIVER_OBJECT DriverObject ) { PVOID Packet; ASSERT(DriverObject); Packet = IoAllocateErrorLogEntry( DriverObject, sizeof(IO_ERROR_LOG_PACKET) + sizeof(SCSIPORT_ALLOCFAILURE_DATA)); return Packet; } VOID FASTCALL SpLogAllocationFailureFn( IN PDRIVER_OBJECT DriverObject, IN POOL_TYPE PoolType, IN SIZE_T Size, IN ULONG Tag, IN ULONG FileId, IN ULONG LineNumber ) /*++ Routine Description: This routine writes a message to the event log indicating that an allocation failure has occurred. Arguments: DriverObject - pointer to the driver object for which the allocation failure event is being logged. PoolType - identifies the pool the failed allocation attempt was from. Size - indicates the number of bytes that the failed allocation attempt tried to obtain. Tag - identifies the pool tag associated with the failed allocation. AllocId - uniquely identifies this allocation w/in scsiport. Return Value: VOID --*/ { NTSTATUS status; PIO_ERROR_LOG_PACKET Packet; PIO_ERROR_LOG_PACKET CurrentValue; SCSIPORT_ALLOCFAILURE_DATA *Data; // PSCSIPORT_ALLOCFAILURE_ENTRY Entry; // PSCSIPORT_ALLOCFAILURE_ENTRY CurrentValue; PSCSIPORT_DRIVER_EXTENSION DriverExtension; DebugPrint((2, "SpLogAllocationFailureFn: DriverObject:%p\nId:%08X|%08X\n", DriverObject, FileId, LineNumber)); // // Try to allocate a new error log event. // Packet = (PIO_ERROR_LOG_PACKET) SpAllocateErrorLogEntry(DriverObject); // // If we could not allocate a log event, we check the driver extension to // see if it has a reserve event we can use. If we cannot get the driver // extension or if it does not contain a reserve event, we return // without logging the allocation failure. // if (Packet == NULL) { // // See if there is a driver extension for this driver. It is possible // that one has not been created yet, so this may fail, in which case // we give up and return. // DriverExtension = IoGetDriverObjectExtension( DriverObject, ScsiPortInitialize ); if (DriverExtension == NULL) { DebugPrint((1, "SpLogAllocationFailureFn: no driver extension\n")); return; } // // Get the reserve event in the driver extension. The reserve event // may have already been used, so it's possible that it is NULL. If // this is the case, we give up and return. // Packet = (PIO_ERROR_LOG_PACKET) DriverExtension->ReserveAllocFailureLogEntry; if (Packet == NULL) { DebugPrint((1, "SpLogAllocationFailureFn: no reserve packet\n")); return; } // // We have to ensure that we are the only instance to use this // event. To do so, we attempt to NULL the event in the driver // extension. If somebody else beats us to it, they own the // event and we have to give up. // CurrentValue = InterlockedCompareExchangePointer( DriverExtension->ReserveAllocFailureLogEntry, NULL, Packet ); if (Packet != CurrentValue) { DebugPrint((1, "SpLogAllocationFailureFn: someone already owns packet\n")); return; } } // // Initialize the error log packet. // Packet->ErrorCode = IO_WARNING_ALLOCATION_FAILED; Packet->SequenceNumber = 0; Packet->MajorFunctionCode = 0; Packet->RetryCount = 0; Packet->UniqueErrorValue = 0x10; Packet->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; Packet->DumpDataSize = sizeof(ULONG) * 4; Packet->NumberOfStrings = 0; Packet->DumpData[0] = Tag; Data = (SCSIPORT_ALLOCFAILURE_DATA*) &Packet->DumpData[1]; Data->Size = (ULONG) Size; Data->FileId = FileId; Data->LineNumber = LineNumber; // // Queue the error log entry. // IoWriteErrorLogEntry(Packet); } PVOID SpAllocatePoolEx( IN POOL_TYPE PoolType, IN SIZE_T NumberOfBytes, IN ULONG Tag, IN PDRIVER_OBJECT DriverObject, IN ULONG FileId, IN ULONG LineNumber ) { PVOID Block; Block = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); if (Block == NULL) { SpLogAllocationFailureFn(DriverObject, PoolType, NumberOfBytes, Tag, FileId, LineNumber); } return Block; } PMDL SpAllocateMdlEx( IN PVOID VirtualAddress, IN ULONG Length, IN BOOLEAN SecondaryBuffer, IN BOOLEAN ChargeQuota, IN OUT PIRP Irp, IN PDRIVER_OBJECT DriverObject, IN ULONG FileId, IN ULONG LineNumber ) { PMDL mdl = IoAllocateMdl(VirtualAddress, Length, SecondaryBuffer, ChargeQuota, Irp); if (mdl == NULL) { SpLogAllocationFailureFn(DriverObject, NonPagedPool, 0, SCSIPORT_TAG_ALLOCMDL, FileId, LineNumber); } return mdl; } PIRP SpAllocateIrpEx( IN CCHAR StackSize, IN BOOLEAN ChargeQuota, IN PDRIVER_OBJECT DriverObject, IN ULONG FileId, IN ULONG LineNumber ) { PIRP irp = IoAllocateIrp(StackSize, ChargeQuota); if (irp == NULL) { SpLogAllocationFailureFn(DriverObject, NonPagedPool, 0, SCSIPORT_TAG_ALLOCIRP, FileId, LineNumber); } return irp; } #ifndef USE_DMA_MACROS VOID SpFreeSGList( IN PADAPTER_EXTENSION Adapter, IN PSRB_DATA SrbData ) { ASSERT(TEST_FLAG(SrbData->Flags, (SRB_DATA_MEDIUM_SG_LIST | SRB_DATA_SMALL_SG_LIST | SRB_DATA_LARGE_SG_LIST))); if (TEST_FLAG(SrbData->Flags, SRB_DATA_MEDIUM_SG_LIST)) { // // Release the scatter gather list back to the lookaside list. // ExFreeToNPagedLookasideList( &(Adapter->MediumScatterGatherLookasideList), (PVOID) SrbData->ScatterGatherList); } else if(TEST_FLAG(SrbData->Flags, SRB_DATA_LARGE_SG_LIST)) { // // We allocated the SG list from pool, so free it. // ExFreePool(SrbData->ScatterGatherList); } else { // // We used the preallocated SG list embedded in the SRB_DATA object, // so we don't have to free anything. // NOTHING } // // Clear the SG-related flags on the SRB_DATA object. // CLEAR_FLAG(SrbData->Flags, (SRB_DATA_LARGE_SG_LIST | SRB_DATA_MEDIUM_SG_LIST | SRB_DATA_SMALL_SG_LIST)); // // NULL the SRB_DATA's SG list pointer. // SrbData->ScatterGatherList = NULL; } #endif