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