6d3c0932b13a2d07265dfa659f4eb98dd0ea2312
[people/sha0/winvblock.git] / src / winvblock / disk / scsi.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * Disk SCSI IRP handling.
26  */
27
28 #include <ntddk.h>
29 #include <scsi.h>
30 #include <ntdddisk.h>
31 #include <ntddcdrm.h>
32
33 #include "winvblock.h"
34 #include "portable.h"
35 #include "driver.h"
36 #include "device.h"
37 #include "disk.h"
38 #include "debug.h"
39
40 /**
41  * The prototype for a disk SCSI function.
42  *
43  * @v dev               Points to the disk's device for the SCSI request.
44  * @v irp               Points to the IRP.
45  * @v disk              Points to the disk for the SCSI request.
46  * @v srb               Points to the SCSI request block.
47  * @v cdb               Points to the command descriptor block.
48  * @v completion        Points to a boolean for whether the IRP has
49  *                      been completed or not.
50  * @ret NTSTATUS        The status of the SCSI operation.
51  */
52 typedef NTSTATUS STDCALL disk_scsi__func(
53     IN WV_SP_DEV_T,
54     IN PIRP,
55     IN disk__type_ptr,
56     IN PSCSI_REQUEST_BLOCK,
57     IN PCDB,
58     OUT winvblock__bool_ptr
59   );
60
61 /* Forward declarations. */
62 disk_scsi__func disk_scsi__read_write_;
63 disk_scsi__func disk_scsi__verify_;
64 disk_scsi__func disk_scsi__read_capacity_;
65 disk_scsi__func disk_scsi__read_capacity_16_;
66 disk_scsi__func disk_scsi__mode_sense_;
67 disk_scsi__func disk_scsi__read_toc_;
68 WV_F_DEV_SCSI disk_scsi__dispatch;
69
70 #if _WIN32_WINNT <= 0x0600
71 #  if 0        /* FIXME: To build with WINDDK 6001.18001 */
72 #    ifdef _MSC_VER
73 #      pragma pack(1)
74 #    endif
75 typedef union _EIGHT_BYTE
76 {
77   struct
78   {
79     winvblock__uint8 Byte0;
80     winvblock__uint8 Byte1;
81     winvblock__uint8 Byte2;
82     winvblock__uint8 Byte3;
83     winvblock__uint8 Byte4;
84     winvblock__uint8 Byte5;
85     winvblock__uint8 Byte6;
86     winvblock__uint8 Byte7;
87   };
88   ULONGLONG AsULongLong;
89 } __attribute__ ( ( __packed__ ) ) EIGHT_BYTE, *PEIGHT_BYTE;
90 #    ifdef _MSC_VER
91 #      pragma pack()
92 #    endif
93 #  endif      /* To build with WINDDK 6001.18001 */
94
95 #  if _WIN32_WINNT < 0x0500
96 #    if 0      /* FIXME: To build with WINDDK 6001.18001 */
97 #      ifdef _MSC_VER
98 #        pragma pack(1)
99 #      endif
100 typedef struct _READ_CAPACITY_DATA_EX
101 {
102   LARGE_INTEGER LogicalBlockAddress;
103   ULONG BytesPerBlock;
104 } __attribute__ ( ( __packed__ ) ) READ_CAPACITY_DATA_EX,
105   *PREAD_CAPACITY_DATA_EX;
106 #      ifdef _MSC_VER
107 #        pragma pack()
108 #      endif
109 #    endif      /* To build with WINDDK 6001.18001 */
110 #  endif      /* _WIN32_WINNT < 0x0500 */
111
112 #  ifdef _MSC_VER
113 #    pragma pack(1)
114 #  endif
115 typedef struct _DISK_CDB16
116 {
117   winvblock__uint8 OperationCode;
118   winvblock__uint8 Reserved1:3;
119   winvblock__uint8 ForceUnitAccess:1;
120   winvblock__uint8 DisablePageOut:1;
121   winvblock__uint8 Protection:3;
122   winvblock__uint8 LogicalBlock[8];
123   winvblock__uint8 TransferLength[4];
124   winvblock__uint8 Reserved2;
125   winvblock__uint8 Control;
126 } __attribute__ ( ( __packed__ ) ) DISK_CDB16, *PDISK_CDB16;
127 #  ifdef _MSC_VER
128 #    pragma pack()
129 #  endif
130
131 #  define REVERSE_BYTES_QUAD(Destination, Source) { \
132   PEIGHT_BYTE d = (PEIGHT_BYTE)(Destination);     \
133   PEIGHT_BYTE s = (PEIGHT_BYTE)(Source);          \
134   d->Byte7 = s->Byte0;                            \
135   d->Byte6 = s->Byte1;                            \
136   d->Byte5 = s->Byte2;                            \
137   d->Byte4 = s->Byte3;                            \
138   d->Byte3 = s->Byte4;                            \
139   d->Byte2 = s->Byte5;                            \
140   d->Byte1 = s->Byte6;                            \
141   d->Byte0 = s->Byte7;                            \
142 }
143 #endif        /* if _WIN32_WINNT <= 0x0600 */
144
145 static NTSTATUS STDCALL disk_scsi__read_write_(
146     IN WV_SP_DEV_T dev,
147     IN PIRP irp,
148     IN disk__type_ptr disk,
149     IN PSCSI_REQUEST_BLOCK srb,
150     IN PCDB cdb,
151     OUT winvblock__bool_ptr completion
152   ) {
153     ULONGLONG start_sector;
154     winvblock__uint32 sector_count;
155     NTSTATUS status = STATUS_SUCCESS;
156
157     if (cdb->AsByte[0] == SCSIOP_READ16 || cdb->AsByte[0] == SCSIOP_WRITE16) {
158         REVERSE_BYTES_QUAD(
159             &start_sector,
160             &(((PDISK_CDB16) cdb)->LogicalBlock[0])
161           );
162         REVERSE_BYTES(
163             &sector_count,
164             &(((PDISK_CDB16) cdb )->TransferLength[0])
165           );
166       } else {
167         start_sector = (cdb->CDB10.LogicalBlockByte0 << 24) +
168           (cdb->CDB10.LogicalBlockByte1 << 16) +
169           (cdb->CDB10.LogicalBlockByte2 << 8 ) +
170           cdb->CDB10.LogicalBlockByte3;
171         sector_count = (cdb->CDB10.TransferBlocksMsb << 8) +
172           cdb->CDB10.TransferBlocksLsb;
173       }
174     if (start_sector >= disk->LBADiskSize) {
175         DBG("Fixed sector_count (start_sector off disk)!!\n");
176         sector_count = 0;
177       }
178     if (
179         (start_sector + sector_count > disk->LBADiskSize) &&
180         sector_count != 0
181       ) {
182         DBG("Fixed sector_count (start_sector + sector_count off disk)!!\n");
183         sector_count = (winvblock__uint32) (disk->LBADiskSize - start_sector);
184       }
185     if (sector_count * disk->SectorSize > srb->DataTransferLength) {
186         DBG("Fixed sector_count (DataTransferLength " "too small)!!\n");
187         sector_count = srb->DataTransferLength / disk->SectorSize;
188       }
189     if (srb->DataTransferLength % disk->SectorSize != 0)
190       DBG("DataTransferLength not aligned!!\n");
191     if (srb->DataTransferLength > sector_count * disk->SectorSize)
192       DBG("DataTransferLength too big!!\n");
193
194     srb->DataTransferLength = sector_count * disk->SectorSize;
195     srb->SrbStatus = SRB_STATUS_SUCCESS;
196     if (sector_count == 0) {
197         irp->IoStatus.Information = 0;
198         return status;
199       }
200
201     if ((
202         (
203             (winvblock__uint8_ptr) srb->DataBuffer -
204             (winvblock__uint8_ptr) MmGetMdlVirtualAddress(irp->MdlAddress)
205           ) + (winvblock__uint8_ptr) MmGetSystemAddressForMdlSafe(
206               irp->MdlAddress,
207               HighPagePriority
208       )) == NULL) {
209         status = STATUS_INSUFFICIENT_RESOURCES;
210         irp->IoStatus.Information = 0;
211         return status;
212       }
213
214     if (cdb->AsByte[0] == SCSIOP_READ || cdb->AsByte[0] == SCSIOP_READ16) {
215         status = disk__io(
216             dev,
217             disk__io_mode_read,
218             start_sector,
219             sector_count,
220             ((winvblock__uint8_ptr) srb->DataBuffer -
221               (winvblock__uint8_ptr) MmGetMdlVirtualAddress(irp->MdlAddress)) +
222               (winvblock__uint8_ptr) MmGetSystemAddressForMdlSafe(
223                   irp->MdlAddress,
224                   HighPagePriority
225               ),
226             irp
227           );
228       } else {
229         status = disk__io(
230             dev,
231             disk__io_mode_write,
232             start_sector,
233             sector_count,
234             ((winvblock__uint8_ptr) srb->DataBuffer -
235               (winvblock__uint8_ptr) MmGetMdlVirtualAddress(irp->MdlAddress)) +
236               (winvblock__uint8_ptr) MmGetSystemAddressForMdlSafe(
237                   irp->MdlAddress,
238                   HighPagePriority
239               ),
240             irp
241           );
242       }
243     if (status != STATUS_PENDING)
244       *completion = TRUE;
245     return status;
246   }
247
248 static NTSTATUS STDCALL disk_scsi__verify_(
249     IN WV_SP_DEV_T dev,
250     IN PIRP irp,
251     IN disk__type_ptr disk,
252     IN PSCSI_REQUEST_BLOCK srb,
253     IN PCDB cdb,
254     OUT winvblock__bool_ptr completion
255   ) {
256     LONGLONG start_sector;
257     winvblock__uint32 sector_count;
258
259     if (cdb->AsByte[0] == SCSIOP_VERIFY16) {
260         REVERSE_BYTES_QUAD(
261             &start_sector,
262             &(((PDISK_CDB16) cdb)->LogicalBlock[0])
263           );
264         REVERSE_BYTES(
265             &sector_count,
266             &(((PDISK_CDB16) cdb)->TransferLength[0])
267           );
268       } else {
269         start_sector = (cdb->CDB10.LogicalBlockByte0 << 24) +
270           (cdb->CDB10.LogicalBlockByte1 << 16) +
271           (cdb->CDB10.LogicalBlockByte2 << 8) +
272           cdb->CDB10.LogicalBlockByte3;
273         sector_count = (cdb->CDB10.TransferBlocksMsb << 8) +
274           cdb->CDB10.TransferBlocksLsb;
275       }
276     #if 0
277     srb->DataTransferLength = sector_count * SECTORSIZE;
278     #endif
279     srb->SrbStatus = SRB_STATUS_SUCCESS;
280     return STATUS_SUCCESS;
281   }
282
283 static NTSTATUS STDCALL disk_scsi__read_capacity_(
284     IN WV_SP_DEV_T dev,
285     IN PIRP irp,
286     IN disk__type_ptr disk,
287     IN PSCSI_REQUEST_BLOCK srb,
288     IN PCDB cdb,
289     OUT winvblock__bool_ptr completion
290   ) {
291     winvblock__uint32 temp = disk->SectorSize;
292     PREAD_CAPACITY_DATA data = (PREAD_CAPACITY_DATA) srb->DataBuffer;
293
294     REVERSE_BYTES(&data->BytesPerBlock, &temp);
295     if ((disk->LBADiskSize - 1) > 0xffffffff) {
296         data->LogicalBlockAddress = -1;
297       } else {
298         temp = (winvblock__uint32) (disk->LBADiskSize - 1);
299         REVERSE_BYTES(&data->LogicalBlockAddress, &temp);
300       }
301     irp->IoStatus.Information = sizeof (READ_CAPACITY_DATA);
302     srb->SrbStatus = SRB_STATUS_SUCCESS;
303     return STATUS_SUCCESS;
304   }
305
306 static NTSTATUS STDCALL disk_scsi__read_capacity_16_(
307     IN WV_SP_DEV_T dev,
308     IN PIRP irp,
309     IN disk__type_ptr disk,
310     IN PSCSI_REQUEST_BLOCK srb,
311     IN PCDB cdb,
312     OUT winvblock__bool_ptr completion
313   ) {
314     winvblock__uint32 temp;
315     LONGLONG big_temp;
316
317     temp = disk->SectorSize;
318     REVERSE_BYTES(
319         &(((PREAD_CAPACITY_DATA_EX) srb->DataBuffer)->BytesPerBlock),
320         &temp
321       );
322     big_temp = disk->LBADiskSize - 1;
323     REVERSE_BYTES_QUAD(
324         &(((PREAD_CAPACITY_DATA_EX) srb->DataBuffer)->
325           LogicalBlockAddress.QuadPart),
326         &big_temp
327       );
328     irp->IoStatus.Information = sizeof (READ_CAPACITY_DATA_EX);
329     srb->SrbStatus = SRB_STATUS_SUCCESS;
330     return STATUS_SUCCESS;
331   }
332
333 static NTSTATUS STDCALL disk_scsi__mode_sense_(
334     IN WV_SP_DEV_T dev,
335     IN PIRP irp,
336     IN disk__type_ptr disk,
337     IN PSCSI_REQUEST_BLOCK srb,
338     IN PCDB cdb,
339     OUT winvblock__bool_ptr completion
340   ) {
341     PMODE_PARAMETER_HEADER mode_param_header;
342     static MEDIA_TYPE media_types[WvDiskMediaTypes] =
343       { RemovableMedia, FixedMedia, RemovableMedia };
344
345     if (srb->DataTransferLength < sizeof (MODE_PARAMETER_HEADER)) {
346         srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
347         return STATUS_SUCCESS;
348       }
349     mode_param_header = (PMODE_PARAMETER_HEADER) srb->DataBuffer;
350     RtlZeroMemory(mode_param_header, srb->DataTransferLength);
351     mode_param_header->ModeDataLength = sizeof (MODE_PARAMETER_HEADER);
352     mode_param_header->MediumType = media_types[disk->Media];
353     mode_param_header->BlockDescriptorLength = 0;
354     srb->DataTransferLength = sizeof (MODE_PARAMETER_HEADER);
355     irp->IoStatus.Information = sizeof (MODE_PARAMETER_HEADER);
356     srb->SrbStatus = SRB_STATUS_SUCCESS;
357     return STATUS_SUCCESS;
358   }
359
360 static NTSTATUS STDCALL disk_scsi__read_toc_(
361     IN WV_SP_DEV_T dev,
362     IN PIRP irp,
363     IN disk__type_ptr disk,
364     IN PSCSI_REQUEST_BLOCK srb,
365     IN PCDB cdb,
366     OUT winvblock__bool_ptr completion
367   ) {
368     /* With thanks to Olof Lagerkvist's ImDisk source. */
369     PCDROM_TOC table_of_contents = (PCDROM_TOC) srb->DataBuffer;
370
371     if (srb->DataTransferLength < sizeof (CDROM_TOC)) {
372         irp->IoStatus.Information = 0;
373         srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
374         return STATUS_BUFFER_TOO_SMALL;
375       }
376     RtlZeroMemory(table_of_contents, sizeof (CDROM_TOC));
377
378     table_of_contents->FirstTrack = 1;
379     table_of_contents->LastTrack = 1;
380     table_of_contents->TrackData[0].Control = 4;
381     irp->IoStatus.Information = sizeof (CDROM_TOC);
382     srb->SrbStatus = SRB_STATUS_SUCCESS;
383     return STATUS_SUCCESS;
384   }
385
386 NTSTATUS STDCALL disk_scsi__dispatch(
387     IN WV_SP_DEV_T dev,
388     IN PIRP irp,
389     IN UCHAR code
390   ) {
391     disk__type_ptr disk = disk__get_ptr(dev);
392     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
393     PSCSI_REQUEST_BLOCK srb = io_stack_loc->Parameters.Scsi.Srb;
394     NTSTATUS status = STATUS_SUCCESS;
395     PCDB cdb;
396     winvblock__bool completion = FALSE;
397
398     srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
399     srb->ScsiStatus = SCSISTAT_GOOD;
400
401     cdb = (PCDB) srb->Cdb;
402
403     irp->IoStatus.Information = 0;
404     if (srb->Lun != 0) {
405         DBG("Invalid Lun!!\n");
406         goto out;
407       }
408     switch (code) {
409         case SRB_FUNCTION_EXECUTE_SCSI:
410           switch (cdb->AsByte[0]) {
411               case SCSIOP_TEST_UNIT_READY:
412                 srb->SrbStatus = SRB_STATUS_SUCCESS;
413                 break;
414
415               case SCSIOP_READ:
416               case SCSIOP_READ16:
417               case SCSIOP_WRITE:
418               case SCSIOP_WRITE16:
419                 status = disk_scsi__read_write_(
420                     dev,
421                     irp,
422                     disk,
423                     srb,
424                     cdb,
425                     &completion
426                   );
427                 break;
428
429               case SCSIOP_VERIFY:
430               case SCSIOP_VERIFY16:
431                 status = disk_scsi__verify_(
432                     dev,
433                     irp,
434                     disk,
435                     srb,
436                     cdb,
437                     &completion
438                   );
439                 break;
440
441               case SCSIOP_READ_CAPACITY:
442                 status = disk_scsi__read_capacity_(
443                     dev,
444                     irp,
445                     disk,
446                     srb,
447                     cdb,
448                     &completion
449                   );
450                 break;
451
452               case SCSIOP_READ_CAPACITY16:
453                 status = disk_scsi__read_capacity_16_(
454                     dev,
455                     irp,
456                     disk,
457                     srb,
458                     cdb,
459                     &completion
460                   );
461                 break;
462
463               case SCSIOP_MODE_SENSE:
464                 status = disk_scsi__mode_sense_(
465                     dev,
466                     irp,
467                     disk,
468                     srb,
469                     cdb,
470                     &completion
471                   );
472                 break;
473
474               case SCSIOP_MEDIUM_REMOVAL:
475                 irp->IoStatus.Information = 0;
476                 srb->SrbStatus = SRB_STATUS_SUCCESS;
477                 status = STATUS_SUCCESS;
478                 break;
479
480               case SCSIOP_READ_TOC:
481                 status = disk_scsi__read_toc_(
482                     dev,
483                     irp,
484                     disk,
485                     srb,
486                     cdb,
487                     &completion
488                   );
489                 break;
490
491               default:
492                 DBG("Invalid SCSIOP (%02x)!!\n", cdb->AsByte[0]);
493                 srb->SrbStatus = SRB_STATUS_ERROR;
494                 status = STATUS_NOT_IMPLEMENTED;
495             }
496           break; /* SRB_FUNCTION_EXECUTE_SCSI */
497
498         case SRB_FUNCTION_IO_CONTROL:
499           srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
500           break;
501
502         case SRB_FUNCTION_CLAIM_DEVICE:
503           srb->DataBuffer = dev->Self;
504           break;
505
506         case SRB_FUNCTION_RELEASE_DEVICE:
507           ObDereferenceObject(dev->Self);
508           break;
509
510         case SRB_FUNCTION_SHUTDOWN:
511         case SRB_FUNCTION_FLUSH:
512           srb->SrbStatus = SRB_STATUS_SUCCESS;
513           break;
514
515         default:
516           DBG("Invalid SRB FUNCTION (%08x)!!\n", code);
517           status = STATUS_NOT_IMPLEMENTED;
518       }
519
520     out:
521     if (!completion) {
522         irp->IoStatus.Status = status;
523         if (status != STATUS_PENDING)
524           IoCompleteRequest(irp, IO_NO_INCREMENT);
525       }
526     return status;
527   }