VOID ScsiIoControl( __in pHW_HBA_EXT pHBAExt, // Adapter device-object extension from port driver. __in PSCSI_REQUEST_BLOCK pSrb, __in PUCHAR pResult, __inout __deref PKIRQL LowestAssumedIrql ) { PSRB_IO_CONTROL srb_io_control = (PSRB_IO_CONTROL)pSrb->DataBuffer; *pResult = ResultDone; if (pSrb->DataTransferLength < sizeof(SRB_IO_CONTROL) || ((srb_io_control->HeaderLength != sizeof(SRB_IO_CONTROL)) | (srb_io_control->HeaderLength + srb_io_control->Length > pSrb->DataTransferLength))) { KdPrint2(("PhDskMnt::ScsiIoControl: Malformed MiniportIOCtl detected.\n", sizeof(srb_io_control->Signature), srb_io_control->Signature)); ScsiSetError(pSrb, SRB_STATUS_INVALID_REQUEST); goto Done; } if (memcmp(srb_io_control->Signature, IMSCSI_FUNCTION_SIGNATURE, sizeof(IMSCSI_FUNCTION_SIGNATURE) - 1)) { KdPrint2(("PhDskMnt::ScsiIoControl: MiniportIOCtl sig '%.*s' not supported\n", sizeof(srb_io_control->Signature), srb_io_control->Signature)); ScsiSetError(pSrb, SRB_STATUS_INVALID_REQUEST); goto Done; } KdPrint2(("PhDskMnt::ScsiIoControl: Miniport IOCtl ControlCode = %#x\n", srb_io_control->ControlCode)); switch (srb_io_control->ControlCode) { case SMP_IMSCSI_CHECK: { KdPrint2(("PhDskMnt::ScsiIoControl: Request to complete SRBs.\n")); srb_io_control->ReturnCode = STATUS_SUCCESS; ScsiSetSuccess(pSrb, 0); break; } case SMP_IMSCSI_CREATE_DEVICE: { PSRB_IMSCSI_CREATE_DATA srb_buffer = (PSRB_IMSCSI_CREATE_DATA)pSrb->DataBuffer; if ((srb_buffer->SrbIoControl.HeaderLength + srb_buffer->SrbIoControl.Length < FIELD_OFFSET(SRB_IMSCSI_CREATE_DATA, Fields.FileName)) || (srb_buffer->Fields.FileNameLength + (ULONG)FIELD_OFFSET(SRB_IMSCSI_CREATE_DATA, Fields.FileName) > pSrb->DataTransferLength)) { KdPrint(("PhDskMnt::ScsiIoControl: Bad SMP_IMSCSI_CREATE_DEVICE request.\n")); ScsiSetError(pSrb, SRB_STATUS_DATA_OVERRUN); goto Done; } ImScsiCreateDevice(pHBAExt, pSrb, pResult, LowestAssumedIrql); break; } case SMP_IMSCSI_REMOVE_DEVICE: { PSRB_IMSCSI_REMOVE_DEVICE srb_buffer = (PSRB_IMSCSI_REMOVE_DEVICE)pSrb->DataBuffer; KdPrint2(("PhDskMnt::ScsiIoControl: Request to remove device.\n")); if (!SRB_IO_CONTROL_SIZE_OK(srb_buffer)) { KdPrint(("PhDskMnt::ScsiIoControl: Bad SMP_IMSCSI_REMOVE_DEVICE request.\n")); ScsiSetError(pSrb, SRB_STATUS_DATA_OVERRUN); goto Done; } srb_io_control->ReturnCode = ImScsiRemoveDevice(pHBAExt, &srb_buffer->DeviceNumber, LowestAssumedIrql); ScsiSetSuccess(pSrb, pSrb->DataTransferLength); break; } case SMP_IMSCSI_QUERY_VERSION: { PSRB_IMSCSI_QUERY_VERSION srb_buffer = (PSRB_IMSCSI_QUERY_VERSION)pSrb->DataBuffer; KdPrint2(("PhDskMnt::ScsiIoControl: Request for driver version.\n")); if (!SRB_IO_CONTROL_SIZE_OK(srb_buffer)) { srb_io_control->ReturnCode = IMSCSI_DRIVER_VERSION; srb_io_control->Length = 0; ScsiSetSuccess(pSrb, pSrb->DataTransferLength); break; } srb_io_control->ReturnCode = IMSCSI_DRIVER_VERSION; srb_io_control->Length = sizeof(*srb_buffer) - sizeof(srb_buffer->SrbIoControl); srb_buffer->SubVersion = PHDSKMNT_VERSION_ULONG; ScsiSetSuccess(pSrb, pSrb->DataTransferLength); break; } case SMP_IMSCSI_QUERY_DEVICE: { PSRB_IMSCSI_CREATE_DATA srb_buffer = (PSRB_IMSCSI_CREATE_DATA)pSrb->DataBuffer; KdPrint2(("PhDskMnt::ScsiIoControl: Request SMP_IMSCSI_QUERY_DEVICE.\n")); if (!SRB_IO_CONTROL_SIZE_OK(srb_buffer)) { KdPrint(("PhDskMnt::ScsiIoControl: Bad SMP_IMSCSI_QUERY_DEVICE request.\n")); pSrb->DataTransferLength = 0; ScsiSetError(pSrb, SRB_STATUS_DATA_OVERRUN); goto Done; } srb_io_control->ReturnCode = ImScsiQueryDevice(pHBAExt, srb_buffer, &pSrb->DataTransferLength, LowestAssumedIrql); ScsiSetSuccess(pSrb, pSrb->DataTransferLength); break; } case SMP_IMSCSI_QUERY_ADAPTER: { PSRB_IMSCSI_QUERY_ADAPTER srb_buffer = (PSRB_IMSCSI_QUERY_ADAPTER)pSrb->DataBuffer; KdPrint2(("PhDskMnt::ScsiIoControl: Request SMP_IMSCSI_QUERY_ADAPTER.\n")); if (!SRB_IO_CONTROL_SIZE_OK(srb_buffer)) { KdPrint(("PhDskMnt::ScsiIoControl: Bad SMP_IMSCSI_QUERY_ADAPTER request.\n")); ScsiSetError(pSrb, SRB_STATUS_DATA_OVERRUN); goto Done; } srb_io_control->ReturnCode = ImScsiQueryAdapter(pHBAExt, srb_buffer, pSrb->DataTransferLength, LowestAssumedIrql); ScsiSetSuccess(pSrb, pSrb->DataTransferLength); break; } case SMP_IMSCSI_SET_DEVICE_FLAGS: { PSRB_IMSCSI_SET_DEVICE_FLAGS srb_buffer = (PSRB_IMSCSI_SET_DEVICE_FLAGS)pSrb->DataBuffer; KdPrint2(("PhDskMnt::ScsiIoControl: Request SMP_IMSCSI_SET_DEVICE_FLAGS.\n")); if (!SRB_IO_CONTROL_SIZE_OK(srb_buffer)) { KdPrint(("PhDskMnt::ScsiIoControl: Bad SMP_IMSCSI_SET_DEVICE_FLAGS request.\n")); ScsiSetError(pSrb, SRB_STATUS_DATA_OVERRUN); goto Done; } srb_io_control->ReturnCode = ImScsiSetFlagsDevice(pHBAExt, srb_buffer, LowestAssumedIrql); ScsiSetSuccess(pSrb, pSrb->DataTransferLength); break; } case SMP_IMSCSI_EXTEND_DEVICE: { PSRB_IMSCSI_EXTEND_DEVICE srb_buffer = (PSRB_IMSCSI_EXTEND_DEVICE)pSrb->DataBuffer; KdPrint2(("PhDskMnt::ScsiIoControl: Request SMP_IMSCSI_EXTEND_DEVICE.\n")); if (!SRB_IO_CONTROL_SIZE_OK(srb_buffer)) { KdPrint(("PhDskMnt::ScsiIoControl: Bad SMP_IMSCSI_EXTEND_DEVICE request.\n")); ScsiSetError(pSrb, SRB_STATUS_DATA_OVERRUN); goto Done; } ImScsiExtendDevice(pHBAExt, pSrb, pResult, LowestAssumedIrql, srb_buffer); break; } default: DbgPrint("PhDskMnt::ScsiExecute: Unknown IOControl code=0x%X\n", srb_io_control->ControlCode); ScsiSetError(pSrb, SRB_STATUS_INVALID_REQUEST); break; } // end switch Done: KdPrint2(("PhDskMnt::ScsiIoControl: End: *Result=%i\n", (INT)*pResult)); return; }
VOID ImScsiDispatchWork( __in pMP_WorkRtnParms pWkRtnParms ) { pHW_HBA_EXT pHBAExt = pWkRtnParms->pHBAExt; pHW_LU_EXTENSION pLUExt = pWkRtnParms->pLUExt; PSCSI_REQUEST_BLOCK pSrb = pWkRtnParms->pSrb; PETHREAD pReqThread = pWkRtnParms->pReqThread; PCDB pCdb = (PCDB)pSrb->Cdb; KdPrint2(("PhDskMnt::ImScsiDispatchWork Action: 0x%X, pSrb: 0x%p, pSrb->DataBuffer: 0x%p pSrb->DataTransferLength: 0x%X\n", (int)pSrb->Cdb[0], pSrb, pSrb->DataBuffer, pSrb->DataTransferLength)); switch (pSrb->Function) { case SRB_FUNCTION_IO_CONTROL: { PSRB_IO_CONTROL srb_io_control = (PSRB_IO_CONTROL)pSrb->DataBuffer; switch (srb_io_control->ControlCode) { case SMP_IMSCSI_CREATE_DEVICE: { // Create new? KIRQL lowest_assumed_irql = PASSIVE_LEVEL; KdPrint(("PhDskMnt::ImScsiDispatchWork: Request to create new device.\n")); ImScsiCreateLU(pHBAExt, pSrb, pReqThread, &lowest_assumed_irql); ObDereferenceObject(pReqThread); } break; default: break; } } break; case SRB_FUNCTION_EXECUTE_SCSI: switch (pSrb->Cdb[0]) { case SCSIOP_READ: case SCSIOP_WRITE: case SCSIOP_READ16: case SCSIOP_WRITE16: { // Read/write? PVOID sysaddress; PVOID buffer; ULONG status; LARGE_INTEGER startingSector; LARGE_INTEGER startingOffset; KLOCK_QUEUE_HANDLE LockHandle; KIRQL lowest_assumed_irql = PASSIVE_LEVEL; if ((pCdb->AsByte[0] == SCSIOP_READ16) | (pCdb->AsByte[0] == SCSIOP_WRITE16)) { REVERSE_BYTES_QUAD(&startingSector, pCdb->CDB16.LogicalBlock); } else { startingSector.QuadPart = 0; REVERSE_BYTES(&startingSector, &pCdb->CDB10.LogicalBlockByte0); } startingOffset.QuadPart = startingSector.QuadPart << pLUExt->BlockPower; KdPrint2(("PhDskMnt::ImScsiDispatchWork starting sector: 0x%I64X\n", startingSector)); status = StoragePortGetSystemAddress(pHBAExt, pSrb, &sysaddress); if ((status != STORAGE_STATUS_SUCCESS) | (sysaddress == NULL)) { DbgPrint("PhDskMnt::ImScsiDispatchWork: StorPortGetSystemAddress failed: status=0x%X address=0x%p translated=0x%p\n", status, pSrb->DataBuffer, sysaddress); pSrb->SrbStatus = SRB_STATUS_ERROR; pSrb->ScsiStatus = SCSISTAT_GOOD; break; } buffer = ExAllocatePoolWithTag(NonPagedPool, pSrb->DataTransferLength, MP_TAG_GENERAL); if (buffer == NULL) { DbgPrint("PhDskMnt::ImScsiDispatchWork: Memory allocation failed.\n"); pSrb->SrbStatus = SRB_STATUS_ERROR; pSrb->ScsiStatus = SCSISTAT_GOOD; break; } else { NTSTATUS status = STATUS_NOT_IMPLEMENTED; /// For write operations, prepare temporary buffer if ((pSrb->Cdb[0] == SCSIOP_WRITE) | (pSrb->Cdb[0] == SCSIOP_WRITE16)) { RtlMoveMemory(buffer, sysaddress, pSrb->DataTransferLength); } if ((pSrb->Cdb[0] == SCSIOP_READ) | (pSrb->Cdb[0] == SCSIOP_READ16)) { status = ImScsiReadDevice(pLUExt, buffer, &startingOffset, &pSrb->DataTransferLength); } else if ((pSrb->Cdb[0] == SCSIOP_WRITE) | (pSrb->Cdb[0] == SCSIOP_WRITE16)) { status = ImScsiWriteDevice(pLUExt, buffer, &startingOffset, &pSrb->DataTransferLength); } if (!NT_SUCCESS(status)) { ExFreePoolWithTag(buffer, MP_TAG_GENERAL); DbgPrint("PhDskMnt::ImScsiDispatchWork: I/O error status=0x%X\n", status); switch (status) { case STATUS_INVALID_BUFFER_SIZE: { DbgPrint("PhDskMnt::ImScsiDispatchWork: STATUS_INVALID_BUFFER_SIZE from image I/O. Reporting SCSI_SENSE_ILLEGAL_REQUEST/SCSI_ADSENSE_INVALID_CDB/0x00.\n"); ScsiSetCheckCondition( pSrb, SRB_STATUS_ERROR, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); break; } case STATUS_DEVICE_BUSY: { DbgPrint("PhDskMnt::ImScsiDispatchWork: STATUS_DEVICE_BUSY from image I/O. Reporting SRB_STATUS_BUSY/SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/SCSI_SENSEQ_BECOMING_READY.\n"); ScsiSetCheckCondition( pSrb, SRB_STATUS_BUSY, SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_BECOMING_READY ); break; } default: { ScsiSetError(pSrb, SRB_STATUS_PARITY_ERROR); break; } } break; } /// Fake random disk signature in case mounted read-only, 0xAA55 at end of mbr and 0x00000000 in disk id field. /// Compatibility fix for mounting Windows Backup vhd files in read-only. if ((pLUExt->FakeDiskSignature != 0) && ((pSrb->Cdb[0] == SCSIOP_READ) | (pSrb->Cdb[0] == SCSIOP_READ16)) && (startingSector.QuadPart == 0) && (pSrb->DataTransferLength >= 512) && (pLUExt->ReadOnly)) { PUCHAR mbr = (PUCHAR)buffer; if ((*(PUSHORT)(mbr + 0x01FE) == 0xAA55) & (*(PUSHORT)(mbr + 0x01BC) == 0x0000) & ((*(mbr + 0x01BE) & 0x7F) == 0x00) & ((*(mbr + 0x01CE) & 0x7F) == 0x00) & ((*(mbr + 0x01DE) & 0x7F) == 0x00) & ((*(mbr + 0x01EE) & 0x7F) == 0x00) & ((*(PULONG)(mbr + 0x01B8) == 0x00000000UL))) { DbgPrint("PhDskMnt::ImScsiDispatchWork: Faking disk signature as %#X.\n", pLUExt->FakeDiskSignature); *(PULONG)(mbr + 0x01B8) = pLUExt->FakeDiskSignature; } } /// For write operations, temporary buffer holds read data. /// Copy that to system buffer. if ((pSrb->Cdb[0] == SCSIOP_READ) | (pSrb->Cdb[0] == SCSIOP_READ16)) { RtlMoveMemory(sysaddress, buffer, pSrb->DataTransferLength); } } ImScsiAcquireLock(&pLUExt->LastIoLock, &LockHandle, lowest_assumed_irql); if (pLUExt->LastIoBuffer != NULL) ExFreePoolWithTag(pLUExt->LastIoBuffer, MP_TAG_GENERAL); pLUExt->LastIoStartSector = startingSector.QuadPart; pLUExt->LastIoLength = pSrb->DataTransferLength; pLUExt->LastIoBuffer = buffer; ImScsiReleaseLock(&LockHandle, &lowest_assumed_irql); ScsiSetSuccess(pSrb, pSrb->DataTransferLength); } break; default: { DbgPrint("PhDskMnt::ImScsiDispatchWork unknown function: 0x%X\n", (int)pSrb->Cdb[0]); ScsiSetError(pSrb, SRB_STATUS_INTERNAL_ERROR); } } default: break; } KdPrint2(("PhDskMnt::ImScsiDispatchWork: End pSrb: 0x%p.\n", pSrb)); } // End ImScsiDispatchWork().