Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

697 lines
19 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 2000
  3. Module Name:
  4. protocol.c
  5. Abstract:
  6. This file contains iSCSI protocol related routines.
  7. Environment:
  8. kernel mode only
  9. Revision History:
  10. --*/
  11. #include "port.h"
  12. LONG GlobalSessionID;
  13. BOOLEAN PrintDataBuffer = FALSE;
  14. UCHAR
  15. GetCdbLength(
  16. IN UCHAR OpCode
  17. );
  18. NTSTATUS
  19. iSpSendLoginResponse(
  20. IN PDEVICE_OBJECT DeviceObject,
  21. IN PVOID Context
  22. )
  23. {
  24. PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
  25. PISCSI_CONNECTION iScsiConnection = fdoExtension->ServerNodeInfo;
  26. PISCSI_LOGIN_RESPONSE iscsiLoginResponse;
  27. PISCSI_LOGIN_COMMAND loginCommand;
  28. NTSTATUS status;
  29. ULONG bytesSent;
  30. ULONG tempULong;
  31. DelayThreadExecution(1);
  32. IoFreeWorkItem((PIO_WORKITEM) Context);
  33. ASSERT((iScsiConnection != NULL));
  34. ASSERT((iScsiConnection->Type) == ISCSI_CONNECTION_TYPE);
  35. loginCommand = (PISCSI_LOGIN_COMMAND)(iScsiConnection->IScsiHeader);
  36. iscsiLoginResponse = iSpAllocatePool(NonPagedPool,
  37. sizeof(ISCSI_LOGIN_RESPONSE),
  38. ISCSI_TAG_LOGIN_RES);
  39. if (iscsiLoginResponse == NULL) {
  40. DebugPrint((0, "Failed to allocate logon response packet\n"));
  41. return STATUS_INSUFFICIENT_RESOURCES;
  42. }
  43. RtlZeroMemory(iscsiLoginResponse, sizeof(ISCSI_LOGIN_RESPONSE));
  44. iscsiLoginResponse->OpCode = ISCSIOP_LOGIN_RESPONSE;
  45. //
  46. // Copy client's Session ID
  47. //
  48. iscsiLoginResponse->ISID[0] = loginCommand->ISID[0];
  49. iscsiLoginResponse->ISID[1] = loginCommand->ISID[1];
  50. tempULong = InterlockedIncrement(&GlobalSessionID);
  51. iscsiLoginResponse->TSID[0] = (UCHAR) ((tempULong & 0xFF00) >> 8);
  52. iscsiLoginResponse->TSID[1] = (UCHAR) (tempULong & 0xFF);
  53. iscsiLoginResponse->ExpCmdRN[0] = loginCommand->InitCmdRN[0];
  54. iscsiLoginResponse->ExpCmdRN[1] = loginCommand->InitCmdRN[1];
  55. iscsiLoginResponse->ExpCmdRN[2] = loginCommand->InitCmdRN[2];
  56. iscsiLoginResponse->ExpCmdRN[3] = loginCommand->InitCmdRN[3];
  57. iscsiLoginResponse->MaxCmdRN[3] = MAX_PENDING_REQUESTS;
  58. iscsiLoginResponse->InitStatRN[3] = 1;
  59. iscsiLoginResponse->Status = ISCSI_LOGINSTATUS_ACCEPT;
  60. GetUlongFromArray((loginCommand->InitCmdRN),
  61. (iScsiConnection->ExpCommandRefNum));
  62. iScsiConnection->MaxCommandRefNum = MAX_PENDING_REQUESTS;
  63. iScsiConnection->StatusRefNum = 1;
  64. //
  65. // Send logon response
  66. //
  67. fdoExtension->CurrentProtocolState = PSFullFeaturePhase;
  68. status = iSpSendData(iScsiConnection->ConnectionDeviceObject,
  69. iScsiConnection->ConnectionFileObject,
  70. iscsiLoginResponse,
  71. sizeof(ISCSI_LOGIN_RESPONSE),
  72. &bytesSent);
  73. if (NT_SUCCESS(status)) {
  74. DebugPrint((3,
  75. "Send succeeded for logon response. Bytes sent : %d\n",
  76. bytesSent));
  77. } else {
  78. DebugPrint((0, "Could not send logon response. Status %x\n",
  79. status));
  80. fdoExtension->CurrentProtocolState = PSLogonFailed;
  81. }
  82. return status;
  83. }
  84. VOID
  85. iSpProcessScsiCommand(
  86. IN PVOID Context
  87. )
  88. {
  89. PISCSI_FDO_EXTENSION fdoExtension = (PISCSI_FDO_EXTENSION) Context;
  90. PISCSI_CONNECTION iScsiConnection = fdoExtension->ServerNodeInfo;
  91. PISCSI_SCSI_COMMAND iScsiCommand;
  92. PISCSI_SCSI_RESPONSE iScsiResponse = NULL;
  93. PIRP irp = NULL;
  94. PMDL mdl = NULL;
  95. PACTIVE_REQUESTS currentRequest;
  96. PIO_STACK_LOCATION irpStack;
  97. PCDB cdb;
  98. SCSI_REQUEST_BLOCK srb;
  99. KEVENT event;
  100. IO_STATUS_BLOCK ioStatus;
  101. ULONG length =0;
  102. ULONG sizeRequired;
  103. ULONG inx;
  104. ULONG bytesSent;
  105. NTSTATUS status;
  106. while (TRUE) {
  107. KeWaitForSingleObject(
  108. (PVOID) &(iScsiConnection->RequestSemaphore),
  109. Executive,
  110. KernelMode,
  111. FALSE,
  112. NULL
  113. );
  114. if ((iScsiConnection->TerminateThread) == TRUE) {
  115. //
  116. // This is an indication to terminate this thread
  117. //
  118. PsTerminateSystemThread(STATUS_SUCCESS);
  119. }
  120. inx = (iScsiConnection->ExpCommandRefNum) % MAX_PENDING_REQUESTS;
  121. if (inx == 0) {
  122. inx = MAX_PENDING_REQUESTS;
  123. }
  124. DebugPrint((3, "Will process request at index %d\n", inx));
  125. currentRequest = &(iScsiConnection->ActiveRequests[inx]);
  126. iScsiCommand = (PISCSI_SCSI_COMMAND) (currentRequest->IScsiHeader);
  127. RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
  128. //
  129. // Set the size of the SCSI Request Block
  130. //
  131. srb.Length = SCSI_REQUEST_BLOCK_SIZE;
  132. srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
  133. if ((iScsiCommand->TurnOffAutoSense) == FALSE) {
  134. srb.SenseInfoBuffer = currentRequest->SenseData;
  135. srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
  136. }
  137. //
  138. // Get the CDB Length based on the CDB OpCode
  139. //
  140. srb.CdbLength = GetCdbLength(iScsiCommand->Cdb[0]);
  141. cdb = (PCDB)(srb.Cdb);
  142. RtlCopyMemory(cdb, iScsiCommand->Cdb, srb.CdbLength);
  143. //
  144. // Set DataBuffer pointer to the command buffer in
  145. // the current request.
  146. //
  147. srb.DataBuffer = currentRequest->CommandBuffer;
  148. GetUlongFromArray((iScsiCommand->Length),
  149. length);
  150. if (length == 0) {
  151. DebugPrint((3, "Length 0. Probably READ command\n"));
  152. GetUlongFromArray((iScsiCommand->ExpDataXferLength),
  153. length);
  154. }
  155. //
  156. // If length is non-zero at this point, then it's
  157. // either a Read (ExpDataXferLength is non-zero), or
  158. // Write (Immediate data length is non-zero) command.
  159. //
  160. if (length != 0) {
  161. DebugPrint((3, "Read or Write data command\n"));
  162. //
  163. // Set the transfer length.
  164. //
  165. srb.DataTransferLength = length;
  166. }
  167. if (iScsiCommand->Read) {
  168. srb.SrbFlags = SRB_FLAGS_DATA_IN;
  169. } else if (length != 0) {
  170. //
  171. // If length is Non-Zero and Read bit is
  172. // NOT set, then it should be a Write command
  173. //
  174. srb.SrbFlags = SRB_FLAGS_DATA_OUT;
  175. }
  176. switch (iScsiCommand->ATTR) {
  177. case ISCSI_TASKATTR_UNTAGGED:
  178. case ISCSI_TASKATTR_SIMPLE : {
  179. srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
  180. break;
  181. }
  182. case ISCSI_TASKATTR_ORDERED: {
  183. srb.QueueAction = SRB_ORDERED_QUEUE_TAG_REQUEST;
  184. break;
  185. }
  186. case ISCSI_TASKATTR_HEADOFQUEUE: {
  187. srb.QueueAction = SRB_HEAD_OF_QUEUE_TAG_REQUEST;
  188. break;
  189. }
  190. default: {
  191. srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
  192. break;
  193. }
  194. }
  195. srb.QueueTag = SP_UNTAGGED;
  196. SET_FLAG(srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
  197. SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
  198. //
  199. // Set the event object to the unsignaled state.
  200. // It will be used to signal request completion.
  201. //
  202. KeInitializeEvent(&event, NotificationEvent, FALSE);
  203. //
  204. // Build device I/O control request with METHOD_NEITHER data transfer.
  205. // We'll queue a completion routine to cleanup the MDL's and such ourself.
  206. //
  207. irp = IoAllocateIrp(
  208. (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
  209. FALSE);
  210. if(irp == NULL) {
  211. DebugPrint((0, "Failed to allocate Irp\n"));
  212. //
  213. // ISSUE : Should handle this failure better
  214. //
  215. continue;
  216. }
  217. //
  218. // Get next stack location.
  219. //
  220. irpStack = IoGetNextIrpStackLocation(irp);
  221. //
  222. // Set up SRB for execute scsi request. Save SRB address in next stack
  223. // for the port driver.
  224. //
  225. irpStack->MajorFunction = IRP_MJ_SCSI;
  226. irpStack->Parameters.Scsi.Srb = &srb;
  227. IoSetCompletionRoutine(irp,
  228. iSpSendSrbSynchronousCompletion,
  229. &srb,
  230. TRUE,
  231. TRUE,
  232. TRUE);
  233. irp->UserIosb = &ioStatus;
  234. irp->UserEvent = &event;
  235. if (srb.DataTransferLength) {
  236. //
  237. // Build an MDL for the data buffer and stick it into the irp. The
  238. // completion routine will unlock the pages and free the MDL.
  239. //
  240. irp->MdlAddress = IoAllocateMdl(srb.DataBuffer,
  241. length,
  242. FALSE,
  243. FALSE,
  244. irp );
  245. if (irp->MdlAddress == NULL) {
  246. IoFreeIrp( irp );
  247. DebugPrint((0, "Failed to allocate MDL\n"));
  248. //
  249. // ISSUE : Should handle this failure better
  250. //
  251. continue;
  252. }
  253. try {
  254. MmProbeAndLockPages( irp->MdlAddress,
  255. KernelMode,
  256. (iScsiCommand->Read ? IoWriteAccess :
  257. IoWriteAccess));
  258. } except(EXCEPTION_EXECUTE_HANDLER) {
  259. status = GetExceptionCode();
  260. IoFreeMdl(irp->MdlAddress);
  261. IoFreeIrp( irp );
  262. DebugPrint((0,
  263. "Could not lock pages. Status : %x\n",
  264. status));
  265. //
  266. // ISSUE : Should handle this failure better
  267. //
  268. continue;
  269. }
  270. }
  271. //
  272. // Set timeout value for this request.
  273. //
  274. // N.B. The value should be chosen depending on
  275. // the type of the device, and type of command.
  276. // For now, just set some reasonable value
  277. //
  278. srb.TimeOutValue = 180;
  279. //
  280. // Zero out status.
  281. //
  282. srb.ScsiStatus = srb.SrbStatus = 0;
  283. srb.NextSrb = 0;
  284. //
  285. // Set up IRP Address.
  286. //
  287. srb.OriginalRequest = irp;
  288. //
  289. // Call the port driver with the request and wait for it to complete.
  290. //
  291. status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
  292. if (status == STATUS_PENDING) {
  293. KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  294. status = ioStatus.Status;
  295. }
  296. //
  297. // Send the response to the client
  298. //
  299. sizeRequired = sizeof(ISCSI_SCSI_RESPONSE);
  300. if (!NT_SUCCESS(status)) {
  301. if (status == STATUS_BUFFER_OVERFLOW) {
  302. if (length >= srb.DataTransferLength) {
  303. DebugPrint((1, "DataUnderrun. Xp Len %d, XFer Len %d\n",
  304. length, srb.DataTransferLength));
  305. status = STATUS_SUCCESS;
  306. sizeRequired += srb.DataTransferLength;
  307. length = srb.DataTransferLength;
  308. } else {
  309. DebugPrint((0,
  310. "Buffer overflow error. XLen %d, XFer Len %d\n",
  311. length, srb.DataTransferLength));
  312. sizeRequired += srb.SenseInfoBufferLength;
  313. length = srb.SenseInfoBufferLength;
  314. }
  315. } else {
  316. DebugPrint((0,
  317. "Command failed. OpCode : 0x%x, Status : 0x%x\n",
  318. srb.Cdb[0],
  319. status));
  320. DebugPrint((1, "Expected length : %d, Transfered length : %d\n",
  321. length, srb.DataTransferLength));
  322. sizeRequired += srb.SenseInfoBufferLength;
  323. length = srb.SenseInfoBufferLength;
  324. }
  325. } else if (iScsiCommand->Read) {
  326. ASSERT((length >= srb.DataTransferLength));
  327. sizeRequired += srb.DataTransferLength;
  328. length = srb.DataTransferLength;
  329. } else {
  330. length = 0;
  331. }
  332. DebugPrint((3, "Size of the response - %d. \n", sizeRequired));
  333. if ((PrintDataBuffer == TRUE) && (length != 0)) {
  334. for (inx = 0; inx < length; inx++) {
  335. DebugPrint((0, "%02x ",
  336. (((PUCHAR)(srb.DataBuffer))[inx])));
  337. if ((inx != 0) && ((inx % 16) == 0)) {
  338. DebugPrint((0, "\n"));
  339. }
  340. }
  341. DebugPrint((0, "\n"));
  342. }
  343. iScsiResponse = iSpAllocatePool(NonPagedPool,
  344. sizeRequired,
  345. ISCSI_TAG_SCSIRES);
  346. if (iScsiResponse != NULL) {
  347. RtlZeroMemory(iScsiResponse, sizeRequired);
  348. iScsiResponse->OpCode = ISCSIOP_SCSI_RESPONSE;
  349. CopyFourBytes((iScsiResponse->TaskTag),
  350. (iScsiCommand->TaskTag));
  351. if (NT_SUCCESS(status)) {
  352. iScsiResponse->CmdStatus = SCSISTAT_GOOD;
  353. iScsiResponse->iSCSIStatus = ISCSISTAT_GOOD;
  354. } else {
  355. DebugPrint((1, "Error. Response data size : %d\n",
  356. sizeRequired));
  357. iScsiResponse->CmdStatus = SCSISTAT_CHECK_CONDITION;
  358. iScsiResponse->iSCSIStatus = ISCSISTAT_CHECK_CONDITION;
  359. }
  360. SetUlongInArray((iScsiResponse->StatusRN),
  361. (iScsiConnection->StatusRefNum));
  362. (iScsiConnection->StatusRefNum)++;
  363. (iScsiConnection->ExpCommandRefNum)++;
  364. SetUlongInArray((iScsiResponse->ExpCmdRN),
  365. (iScsiConnection->ExpCommandRefNum));
  366. SetUlongInArray((iScsiResponse->MaxCmdRN),
  367. (iScsiConnection->ExpCommandRefNum) +
  368. (MAX_PENDING_REQUESTS) - 1);
  369. if (!NT_SUCCESS(status)) {
  370. if (srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
  371. DebugPrint((1, "Sense Data is valid\n"));
  372. if ((iScsiCommand->TurnOffAutoSense) == FALSE) {
  373. ULONG inx;
  374. RtlCopyMemory((iScsiResponse + 1),
  375. srb.SenseInfoBuffer,
  376. srb.SenseInfoBufferLength);
  377. DebugPrint((0, "OpCode : %x, Sense Data : ",
  378. srb.Cdb[0]));
  379. for (inx = 0; inx < (srb.SenseInfoBufferLength); inx++) {
  380. DebugPrint((0, "%02x ", ((PUCHAR)(srb.SenseInfoBuffer))[inx]));
  381. }
  382. DebugPrint((0, "\n"));
  383. SetUlongInArray((iScsiResponse->Length),
  384. srb.SenseInfoBufferLength);
  385. iScsiResponse->SenseDataLength[0] =
  386. (UCHAR) ((srb.SenseInfoBufferLength) & 0x0000FF00);
  387. iScsiResponse->SenseDataLength[1] =
  388. (UCHAR) ((srb.SenseInfoBufferLength) & 0x000000FF);
  389. }
  390. } else {
  391. ULONG inx0, inx1;
  392. PUCHAR responseBuffer = (PUCHAR) iScsiResponse;
  393. DebugPrint((0, "Sense Data is NOT valid\n"));
  394. length = 0;
  395. sizeRequired = sizeof(ISCSI_SCSI_RESPONSE);
  396. DebugPrint((1, "\n Beginning Of Data\n"));
  397. inx0 = 0;
  398. while (inx0 < sizeRequired) {
  399. inx1 = 0;
  400. DebugPrint((1, "\t"));
  401. while ((inx1 < 4) && ((inx0+inx1) < sizeRequired)) {
  402. DebugPrint((1, "%02x ",
  403. responseBuffer[inx0+inx1]));
  404. inx1++;
  405. }
  406. DebugPrint((1, "\n"));
  407. inx0 += 4;
  408. }
  409. DebugPrint((1, " End Of Data\n"));
  410. }
  411. } else if (length != 0) {
  412. RtlCopyMemory((iScsiResponse + 1),
  413. srb.DataBuffer,
  414. length);
  415. SetUlongInArray((iScsiResponse->Length),
  416. length);
  417. iScsiResponse->ResponseLength[0] = (UCHAR) (length & 0x0000FF00);
  418. iScsiResponse->ResponseLength[1] = (UCHAR) (length & 0x000000FF);
  419. }
  420. status = iSpSendData(iScsiConnection->ConnectionDeviceObject,
  421. iScsiConnection->ConnectionFileObject,
  422. iScsiResponse,
  423. sizeRequired,
  424. &bytesSent);
  425. if (NT_SUCCESS(status)) {
  426. DebugPrint((3,
  427. "Successfully sent SCSI Response. Bytes sent : %d\n",
  428. bytesSent));
  429. } else {
  430. DebugPrint((0, "Failed to send SCSI response. Status : 0x%x\n",
  431. status));
  432. }
  433. }
  434. if (irp->MdlAddress) {
  435. MmUnlockPages(irp->MdlAddress);
  436. IoFreeMdl(irp->MdlAddress);
  437. }
  438. IoFreeIrp( irp );
  439. }
  440. return;
  441. }
  442. NTSTATUS
  443. iSpSendSrbSynchronousCompletion(
  444. PDEVICE_OBJECT DeviceObject,
  445. PIRP Irp,
  446. PVOID Context
  447. )
  448. {
  449. *(Irp->UserIosb) = Irp->IoStatus;
  450. //
  451. // Signal the caller's event.
  452. //
  453. KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
  454. return STATUS_MORE_PROCESSING_REQUIRED;
  455. }
  456. UCHAR
  457. GetCdbLength(
  458. IN UCHAR OpCode
  459. )
  460. {
  461. UCHAR commandGroup;
  462. commandGroup = (OpCode >> 5) & 0x07;
  463. DebugPrint((3, "Command Group - %d\n", commandGroup));
  464. switch (commandGroup) {
  465. case COMMAND_GROUP_0: {
  466. return CDB6GENERIC_LENGTH;
  467. }
  468. case COMMAND_GROUP_1:
  469. case COMMAND_GROUP_2: {
  470. return CDB10GENERIC_LENGTH;
  471. }
  472. case COMMAND_GROUP_5: {
  473. return CDB12GENERIC_LENGTH;
  474. }
  475. default: {
  476. ASSERTMSG("Unknown CDB Opcode type\n",
  477. FALSE);
  478. }
  479. } // switch (commandGroup)
  480. return 0;
  481. }
  482. ULONG
  483. iSpGetActiveClientRequestIndex(
  484. IN PISCSI_CONNECTION IScsiConnection,
  485. IN PISCSI_SCSI_COMMAND IScsiCommand
  486. )
  487. {
  488. ULONG cmdRefNum;
  489. ULONG expCmdRefNum;
  490. ULONG inx;
  491. GetUlongFromArray((IScsiCommand->CmdRN),
  492. cmdRefNum);
  493. expCmdRefNum = IScsiConnection->ExpCommandRefNum;
  494. if ((cmdRefNum < expCmdRefNum) ||
  495. (cmdRefNum >= (expCmdRefNum + MAX_PENDING_REQUESTS))) {
  496. DebugPrint((0, "Unexpected Command Ref Num : %d",
  497. cmdRefNum));
  498. ASSERT(FALSE);
  499. }
  500. inx = cmdRefNum % MAX_PENDING_REQUESTS;
  501. if (inx == 0) {
  502. inx = MAX_PENDING_REQUESTS;
  503. }
  504. DebugPrint((3, "Will copy request to slot %d\n", inx));
  505. return inx;
  506. }