• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   SCSI disk driver that layers on every SCSI IO protocol in the system.
3 
4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 
16 #include "ScsiDisk.h"
17 
18 EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {
19   ScsiDiskDriverBindingSupported,
20   ScsiDiskDriverBindingStart,
21   ScsiDiskDriverBindingStop,
22   0xa,
23   NULL,
24   NULL
25 };
26 
27 EFI_DISK_INFO_PROTOCOL gScsiDiskInfoProtocolTemplate = {
28   EFI_DISK_INFO_SCSI_INTERFACE_GUID,
29   ScsiDiskInfoInquiry,
30   ScsiDiskInfoIdentify,
31   ScsiDiskInfoSenseData,
32   ScsiDiskInfoWhichIde
33 };
34 
35 /**
36   Allocates an aligned buffer for SCSI disk.
37 
38   This function allocates an aligned buffer for the SCSI disk to perform
39   SCSI IO operations. The alignment requirement is from SCSI IO interface.
40 
41   @param  ScsiDiskDevice    The SCSI disk involved for the operation.
42   @param  BufferSize        The request buffer size.
43 
44   @return A pointer to the aligned buffer or NULL if the allocation fails.
45 
46 **/
47 VOID *
AllocateAlignedBuffer(IN SCSI_DISK_DEV * ScsiDiskDevice,IN UINTN BufferSize)48 AllocateAlignedBuffer (
49   IN SCSI_DISK_DEV            *ScsiDiskDevice,
50   IN UINTN                    BufferSize
51   )
52 {
53   return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiDiskDevice->ScsiIo->IoAlign);
54 }
55 
56 /**
57   Frees an aligned buffer for SCSI disk.
58 
59   This function frees an aligned buffer for the SCSI disk to perform
60   SCSI IO operations.
61 
62   @param  Buffer            The aligned buffer to be freed.
63   @param  BufferSize        The request buffer size.
64 
65 **/
66 VOID
FreeAlignedBuffer(IN VOID * Buffer,IN UINTN BufferSize)67 FreeAlignedBuffer (
68   IN VOID                     *Buffer,
69   IN UINTN                    BufferSize
70   )
71 {
72   if (Buffer != NULL) {
73     FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize));
74   }
75 }
76 
77 /**
78   The user Entry Point for module ScsiDisk.
79 
80   The user code starts with this function.
81 
82   @param  ImageHandle    The firmware allocated handle for the EFI image.
83   @param  SystemTable    A pointer to the EFI System Table.
84 
85   @retval EFI_SUCCESS       The entry point is executed successfully.
86   @retval other             Some error occurs when executing this entry point.
87 
88 **/
89 EFI_STATUS
90 EFIAPI
InitializeScsiDisk(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)91 InitializeScsiDisk(
92   IN EFI_HANDLE           ImageHandle,
93   IN EFI_SYSTEM_TABLE     *SystemTable
94   )
95 {
96   EFI_STATUS              Status;
97 
98   //
99   // Install driver model protocol(s).
100   //
101   Status = EfiLibInstallDriverBindingComponentName2 (
102              ImageHandle,
103              SystemTable,
104              &gScsiDiskDriverBinding,
105              ImageHandle,
106              &gScsiDiskComponentName,
107              &gScsiDiskComponentName2
108              );
109   ASSERT_EFI_ERROR (Status);
110 
111 
112   return Status;
113 }
114 
115 /**
116   Test to see if this driver supports ControllerHandle.
117 
118   This service is called by the EFI boot service ConnectController(). In order
119   to make drivers as small as possible, there are a few calling restrictions for
120   this service. ConnectController() must follow these calling restrictions.
121   If any other agent wishes to call Supported() it must also follow these
122   calling restrictions.
123 
124   @param  This                Protocol instance pointer.
125   @param  ControllerHandle    Handle of device to test
126   @param  RemainingDevicePath Optional parameter use to pick a specific child
127                               device to start.
128 
129   @retval EFI_SUCCESS         This driver supports this device
130   @retval EFI_ALREADY_STARTED This driver is already running on this device
131   @retval other               This driver does not support this device
132 
133 **/
134 EFI_STATUS
135 EFIAPI
ScsiDiskDriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)136 ScsiDiskDriverBindingSupported (
137   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
138   IN EFI_HANDLE                   Controller,
139   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL
140   )
141 {
142   EFI_STATUS            Status;
143   EFI_SCSI_IO_PROTOCOL  *ScsiIo;
144   UINT8                 DeviceType;
145 
146   Status = gBS->OpenProtocol (
147                   Controller,
148                   &gEfiScsiIoProtocolGuid,
149                   (VOID **) &ScsiIo,
150                   This->DriverBindingHandle,
151                   Controller,
152                   EFI_OPEN_PROTOCOL_BY_DRIVER
153                   );
154   if (EFI_ERROR (Status)) {
155     return Status;
156   }
157 
158   Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType);
159   if (!EFI_ERROR (Status)) {
160     if ((DeviceType == EFI_SCSI_TYPE_DISK) || (DeviceType == EFI_SCSI_TYPE_CDROM)) {
161       Status = EFI_SUCCESS;
162     } else {
163       Status = EFI_UNSUPPORTED;
164     }
165   }
166 
167   gBS->CloseProtocol (
168          Controller,
169          &gEfiScsiIoProtocolGuid,
170          This->DriverBindingHandle,
171          Controller
172          );
173   return Status;
174 }
175 
176 
177 /**
178   Start this driver on ControllerHandle.
179 
180   This service is called by the EFI boot service ConnectController(). In order
181   to make drivers as small as possible, there are a few calling restrictions for
182   this service. ConnectController() must follow these calling restrictions. If
183   any other agent wishes to call Start() it must also follow these calling
184   restrictions.
185 
186   @param  This                 Protocol instance pointer.
187   @param  ControllerHandle     Handle of device to bind driver to
188   @param  RemainingDevicePath  Optional parameter use to pick a specific child
189                                device to start.
190 
191   @retval EFI_SUCCESS          This driver is added to ControllerHandle
192   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle
193   @retval other                This driver does not support this device
194 
195 **/
196 EFI_STATUS
197 EFIAPI
ScsiDiskDriverBindingStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)198 ScsiDiskDriverBindingStart (
199   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
200   IN EFI_HANDLE                   Controller,
201   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL
202   )
203 {
204   EFI_STATUS            Status;
205   EFI_SCSI_IO_PROTOCOL  *ScsiIo;
206   SCSI_DISK_DEV         *ScsiDiskDevice;
207   BOOLEAN               Temp;
208   UINT8                 Index;
209   UINT8                 MaxRetry;
210   BOOLEAN               NeedRetry;
211   BOOLEAN               MustReadCapacity;
212 
213   MustReadCapacity = TRUE;
214 
215   ScsiDiskDevice = (SCSI_DISK_DEV *) AllocateZeroPool (sizeof (SCSI_DISK_DEV));
216   if (ScsiDiskDevice == NULL) {
217     return EFI_OUT_OF_RESOURCES;
218   }
219 
220   Status = gBS->OpenProtocol (
221                   Controller,
222                   &gEfiScsiIoProtocolGuid,
223                   (VOID **) &ScsiIo,
224                   This->DriverBindingHandle,
225                   Controller,
226                   EFI_OPEN_PROTOCOL_BY_DRIVER
227                   );
228   if (EFI_ERROR (Status)) {
229     FreePool (ScsiDiskDevice);
230     return Status;
231   }
232 
233   ScsiDiskDevice->Signature            = SCSI_DISK_DEV_SIGNATURE;
234   ScsiDiskDevice->ScsiIo               = ScsiIo;
235   ScsiDiskDevice->BlkIo.Revision       = EFI_BLOCK_IO_PROTOCOL_REVISION3;
236   ScsiDiskDevice->BlkIo.Media          = &ScsiDiskDevice->BlkIoMedia;
237   ScsiDiskDevice->BlkIo.Media->IoAlign = ScsiIo->IoAlign;
238   ScsiDiskDevice->BlkIo.Reset          = ScsiDiskReset;
239   ScsiDiskDevice->BlkIo.ReadBlocks     = ScsiDiskReadBlocks;
240   ScsiDiskDevice->BlkIo.WriteBlocks    = ScsiDiskWriteBlocks;
241   ScsiDiskDevice->BlkIo.FlushBlocks    = ScsiDiskFlushBlocks;
242   ScsiDiskDevice->BlkIo2.Media         = &ScsiDiskDevice->BlkIoMedia;
243   ScsiDiskDevice->BlkIo2.Reset         = ScsiDiskResetEx;
244   ScsiDiskDevice->BlkIo2.ReadBlocksEx  = ScsiDiskReadBlocksEx;
245   ScsiDiskDevice->BlkIo2.WriteBlocksEx = ScsiDiskWriteBlocksEx;
246   ScsiDiskDevice->BlkIo2.FlushBlocksEx = ScsiDiskFlushBlocksEx;
247   ScsiDiskDevice->Handle               = Controller;
248   InitializeListHead (&ScsiDiskDevice->BlkIo2Queue);
249 
250   ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));
251   switch (ScsiDiskDevice->DeviceType) {
252   case EFI_SCSI_TYPE_DISK:
253     ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;
254     MustReadCapacity = TRUE;
255     break;
256 
257   case EFI_SCSI_TYPE_CDROM:
258     ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;
259     ScsiDiskDevice->BlkIo.Media->ReadOnly  = TRUE;
260     MustReadCapacity = FALSE;
261     break;
262   }
263   //
264   // The Sense Data Array's initial size is 6
265   //
266   ScsiDiskDevice->SenseDataNumber = 6;
267   ScsiDiskDevice->SenseData = (EFI_SCSI_SENSE_DATA *) AllocateZeroPool (
268                                  sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber
269                                  );
270   if (ScsiDiskDevice->SenseData == NULL) {
271     gBS->CloseProtocol (
272           Controller,
273           &gEfiScsiIoProtocolGuid,
274           This->DriverBindingHandle,
275           Controller
276           );
277     FreePool (ScsiDiskDevice);
278     return EFI_OUT_OF_RESOURCES;
279   }
280 
281   //
282   // Retrieve device information
283   //
284   MaxRetry = 2;
285   for (Index = 0; Index < MaxRetry; Index++) {
286     Status = ScsiDiskInquiryDevice (ScsiDiskDevice, &NeedRetry);
287     if (!EFI_ERROR (Status)) {
288       break;
289     }
290 
291     if (!NeedRetry) {
292       FreePool (ScsiDiskDevice->SenseData);
293       gBS->CloseProtocol (
294              Controller,
295              &gEfiScsiIoProtocolGuid,
296              This->DriverBindingHandle,
297              Controller
298              );
299       FreePool (ScsiDiskDevice);
300       return EFI_DEVICE_ERROR;
301     }
302   }
303   //
304   // The second parameter "TRUE" means must
305   // retrieve media capacity
306   //
307   Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);
308   if (!EFI_ERROR (Status)) {
309     //
310     // Determine if Block IO & Block IO2 should be produced on this controller
311     // handle
312     //
313     if (DetermineInstallBlockIo(Controller)) {
314       InitializeInstallDiskInfo(ScsiDiskDevice, Controller);
315       Status = gBS->InstallMultipleProtocolInterfaces (
316                       &Controller,
317                       &gEfiBlockIoProtocolGuid,
318                       &ScsiDiskDevice->BlkIo,
319                       &gEfiBlockIo2ProtocolGuid,
320                       &ScsiDiskDevice->BlkIo2,
321                       &gEfiDiskInfoProtocolGuid,
322                       &ScsiDiskDevice->DiskInfo,
323                       NULL
324                       );
325       if (!EFI_ERROR(Status)) {
326         ScsiDiskDevice->ControllerNameTable = NULL;
327         AddUnicodeString2 (
328           "eng",
329           gScsiDiskComponentName.SupportedLanguages,
330           &ScsiDiskDevice->ControllerNameTable,
331           L"SCSI Disk Device",
332           TRUE
333           );
334         AddUnicodeString2 (
335           "en",
336           gScsiDiskComponentName2.SupportedLanguages,
337           &ScsiDiskDevice->ControllerNameTable,
338           L"SCSI Disk Device",
339           FALSE
340           );
341         return EFI_SUCCESS;
342       }
343     }
344   }
345 
346   gBS->FreePool (ScsiDiskDevice->SenseData);
347   gBS->FreePool (ScsiDiskDevice);
348   gBS->CloseProtocol (
349          Controller,
350          &gEfiScsiIoProtocolGuid,
351          This->DriverBindingHandle,
352          Controller
353          );
354   return Status;
355 
356 }
357 
358 
359 /**
360   Stop this driver on ControllerHandle.
361 
362   This service is called by the EFI boot service DisconnectController().
363   In order to make drivers as small as possible, there are a few calling
364   restrictions for this service. DisconnectController() must follow these
365   calling restrictions. If any other agent wishes to call Stop() it must
366   also follow these calling restrictions.
367 
368   @param  This              Protocol instance pointer.
369   @param  ControllerHandle  Handle of device to stop driver on
370   @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
371                             children is zero stop the entire bus driver.
372   @param  ChildHandleBuffer List of Child Handles to Stop.
373 
374   @retval EFI_SUCCESS       This driver is removed ControllerHandle
375   @retval other             This driver was not removed from this device
376 
377 **/
378 EFI_STATUS
379 EFIAPI
ScsiDiskDriverBindingStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer OPTIONAL)380 ScsiDiskDriverBindingStop (
381   IN  EFI_DRIVER_BINDING_PROTOCOL     *This,
382   IN  EFI_HANDLE                      Controller,
383   IN  UINTN                           NumberOfChildren,
384   IN  EFI_HANDLE                      *ChildHandleBuffer   OPTIONAL
385   )
386 {
387   EFI_BLOCK_IO_PROTOCOL *BlkIo;
388   SCSI_DISK_DEV         *ScsiDiskDevice;
389   EFI_STATUS            Status;
390 
391   Status = gBS->OpenProtocol (
392                   Controller,
393                   &gEfiBlockIoProtocolGuid,
394                   (VOID **) &BlkIo,
395                   This->DriverBindingHandle,
396                   Controller,
397                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
398                   );
399   if (EFI_ERROR (Status)) {
400     return Status;
401   }
402 
403   ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (BlkIo);
404 
405   //
406   // Wait for the BlockIo2 requests queue to become empty
407   //
408   while (!IsListEmpty (&ScsiDiskDevice->BlkIo2Queue));
409 
410   Status = gBS->UninstallMultipleProtocolInterfaces (
411                   Controller,
412                   &gEfiBlockIoProtocolGuid,
413                   &ScsiDiskDevice->BlkIo,
414                   &gEfiBlockIo2ProtocolGuid,
415                   &ScsiDiskDevice->BlkIo2,
416                   &gEfiDiskInfoProtocolGuid,
417                   &ScsiDiskDevice->DiskInfo,
418                   NULL
419                   );
420   if (!EFI_ERROR (Status)) {
421     gBS->CloseProtocol (
422            Controller,
423            &gEfiScsiIoProtocolGuid,
424            This->DriverBindingHandle,
425            Controller
426            );
427 
428     ReleaseScsiDiskDeviceResources (ScsiDiskDevice);
429 
430     return EFI_SUCCESS;
431   }
432   //
433   // errors met
434   //
435   return Status;
436 }
437 
438 /**
439   Reset SCSI Disk.
440 
441 
442   @param  This                 The pointer of EFI_BLOCK_IO_PROTOCOL
443   @param  ExtendedVerification The flag about if extend verificate
444 
445   @retval EFI_SUCCESS          The device was reset.
446   @retval EFI_DEVICE_ERROR     The device is not functioning properly and could
447                                not be reset.
448   @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().
449 
450 **/
451 EFI_STATUS
452 EFIAPI
ScsiDiskReset(IN EFI_BLOCK_IO_PROTOCOL * This,IN BOOLEAN ExtendedVerification)453 ScsiDiskReset (
454   IN  EFI_BLOCK_IO_PROTOCOL   *This,
455   IN  BOOLEAN                 ExtendedVerification
456   )
457 {
458   EFI_TPL       OldTpl;
459   SCSI_DISK_DEV *ScsiDiskDevice;
460   EFI_STATUS    Status;
461 
462   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
463 
464   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_BLKIO (This);
465 
466   Status          = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
467 
468   if (EFI_ERROR (Status)) {
469     Status = EFI_DEVICE_ERROR;
470     goto Done;
471   }
472 
473   if (!ExtendedVerification) {
474     goto Done;
475   }
476 
477   Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
478 
479   if (EFI_ERROR (Status)) {
480     Status = EFI_DEVICE_ERROR;
481     goto Done;
482   }
483 
484 Done:
485   gBS->RestoreTPL (OldTpl);
486   return Status;
487 }
488 
489 /**
490   The function is to Read Block from SCSI Disk.
491 
492   @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL.
493   @param  MediaId    The Id of Media detected
494   @param  Lba        The logic block address
495   @param  BufferSize The size of Buffer
496   @param  Buffer     The buffer to fill the read out data
497 
498   @retval EFI_SUCCESS           Successfully to read out block.
499   @retval EFI_DEVICE_ERROR      Fail to detect media.
500   @retval EFI_NO_MEDIA          Media is not present.
501   @retval EFI_MEDIA_CHANGED     Media has changed.
502   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
503   @retval EFI_INVALID_PARAMETER Invalid parameter passed in.
504 
505 **/
506 EFI_STATUS
507 EFIAPI
ScsiDiskReadBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)508 ScsiDiskReadBlocks (
509   IN  EFI_BLOCK_IO_PROTOCOL   *This,
510   IN  UINT32                  MediaId,
511   IN  EFI_LBA                 Lba,
512   IN  UINTN                   BufferSize,
513   OUT VOID                    *Buffer
514   )
515 {
516   SCSI_DISK_DEV       *ScsiDiskDevice;
517   EFI_BLOCK_IO_MEDIA  *Media;
518   EFI_STATUS          Status;
519   UINTN               BlockSize;
520   UINTN               NumberOfBlocks;
521   BOOLEAN             MediaChange;
522   EFI_TPL             OldTpl;
523 
524   MediaChange    = FALSE;
525   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);
526   ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);
527 
528   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
529 
530     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);
531     if (EFI_ERROR (Status)) {
532       Status = EFI_DEVICE_ERROR;
533       goto Done;
534     }
535 
536     if (MediaChange) {
537       gBS->ReinstallProtocolInterface (
538             ScsiDiskDevice->Handle,
539             &gEfiBlockIoProtocolGuid,
540             &ScsiDiskDevice->BlkIo,
541             &ScsiDiskDevice->BlkIo
542             );
543       gBS->ReinstallProtocolInterface (
544              ScsiDiskDevice->Handle,
545              &gEfiBlockIo2ProtocolGuid,
546              &ScsiDiskDevice->BlkIo2,
547              &ScsiDiskDevice->BlkIo2
548              );
549       Status = EFI_MEDIA_CHANGED;
550       goto Done;
551     }
552   }
553   //
554   // Get the intrinsic block size
555   //
556   Media           = ScsiDiskDevice->BlkIo.Media;
557   BlockSize       = Media->BlockSize;
558 
559   NumberOfBlocks  = BufferSize / BlockSize;
560 
561   if (!(Media->MediaPresent)) {
562     Status = EFI_NO_MEDIA;
563     goto Done;
564   }
565 
566   if (MediaId != Media->MediaId) {
567     Status = EFI_MEDIA_CHANGED;
568     goto Done;
569   }
570 
571   if (Buffer == NULL) {
572     Status = EFI_INVALID_PARAMETER;
573     goto Done;
574   }
575 
576   if (BufferSize == 0) {
577     Status = EFI_SUCCESS;
578     goto Done;
579   }
580 
581   if (BufferSize % BlockSize != 0) {
582     Status = EFI_BAD_BUFFER_SIZE;
583     goto Done;
584   }
585 
586   if (Lba > Media->LastBlock) {
587     Status = EFI_INVALID_PARAMETER;
588     goto Done;
589   }
590 
591   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
592     Status = EFI_INVALID_PARAMETER;
593     goto Done;
594   }
595 
596   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
597     Status = EFI_INVALID_PARAMETER;
598     goto Done;
599   }
600 
601   //
602   // If all the parameters are valid, then perform read sectors command
603   // to transfer data from device to host.
604   //
605   Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);
606 
607 Done:
608   gBS->RestoreTPL (OldTpl);
609   return Status;
610 }
611 
612 /**
613   The function is to Write Block to SCSI Disk.
614 
615   @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL
616   @param  MediaId    The Id of Media detected
617   @param  Lba        The logic block address
618   @param  BufferSize The size of Buffer
619   @param  Buffer     The buffer to fill the read out data
620 
621   @retval EFI_SUCCESS           Successfully to read out block.
622   @retval EFI_WRITE_PROTECTED   The device can not be written to.
623   @retval EFI_DEVICE_ERROR      Fail to detect media.
624   @retval EFI_NO_MEDIA          Media is not present.
625   @retval EFI_MEDIA_CHNAGED     Media has changed.
626   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
627   @retval EFI_INVALID_PARAMETER Invalid parameter passed in.
628 
629 **/
630 EFI_STATUS
631 EFIAPI
ScsiDiskWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN VOID * Buffer)632 ScsiDiskWriteBlocks (
633   IN  EFI_BLOCK_IO_PROTOCOL   *This,
634   IN  UINT32                  MediaId,
635   IN  EFI_LBA                 Lba,
636   IN  UINTN                   BufferSize,
637   IN  VOID                    *Buffer
638   )
639 {
640   SCSI_DISK_DEV       *ScsiDiskDevice;
641   EFI_BLOCK_IO_MEDIA  *Media;
642   EFI_STATUS          Status;
643   UINTN               BlockSize;
644   UINTN               NumberOfBlocks;
645   BOOLEAN             MediaChange;
646   EFI_TPL             OldTpl;
647 
648   MediaChange    = FALSE;
649   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);
650   ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);
651 
652   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
653 
654     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);
655     if (EFI_ERROR (Status)) {
656       Status = EFI_DEVICE_ERROR;
657       goto Done;
658     }
659 
660     if (MediaChange) {
661       gBS->ReinstallProtocolInterface (
662             ScsiDiskDevice->Handle,
663             &gEfiBlockIoProtocolGuid,
664             &ScsiDiskDevice->BlkIo,
665             &ScsiDiskDevice->BlkIo
666             );
667       gBS->ReinstallProtocolInterface (
668              ScsiDiskDevice->Handle,
669              &gEfiBlockIo2ProtocolGuid,
670              &ScsiDiskDevice->BlkIo2,
671              &ScsiDiskDevice->BlkIo2
672              );
673       Status = EFI_MEDIA_CHANGED;
674       goto Done;
675     }
676   }
677   //
678   // Get the intrinsic block size
679   //
680   Media           = ScsiDiskDevice->BlkIo.Media;
681   BlockSize       = Media->BlockSize;
682 
683   NumberOfBlocks  = BufferSize / BlockSize;
684 
685   if (!(Media->MediaPresent)) {
686     Status = EFI_NO_MEDIA;
687     goto Done;
688   }
689 
690   if (MediaId != Media->MediaId) {
691     Status = EFI_MEDIA_CHANGED;
692     goto Done;
693   }
694 
695   if (Media->ReadOnly) {
696     Status = EFI_WRITE_PROTECTED;
697     goto Done;
698   }
699 
700   if (BufferSize == 0) {
701     Status = EFI_SUCCESS;
702     goto Done;
703   }
704 
705   if (Buffer == NULL) {
706     Status = EFI_INVALID_PARAMETER;
707     goto Done;
708   }
709 
710   if (BufferSize % BlockSize != 0) {
711     Status = EFI_BAD_BUFFER_SIZE;
712     goto Done;
713   }
714 
715   if (Lba > Media->LastBlock) {
716     Status = EFI_INVALID_PARAMETER;
717     goto Done;
718   }
719 
720   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
721     Status = EFI_INVALID_PARAMETER;
722     goto Done;
723   }
724 
725   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
726     Status = EFI_INVALID_PARAMETER;
727     goto Done;
728   }
729   //
730   // if all the parameters are valid, then perform read sectors command
731   // to transfer data from device to host.
732   //
733   Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);
734 
735 Done:
736   gBS->RestoreTPL (OldTpl);
737   return Status;
738 }
739 
740 /**
741   Flush Block to Disk.
742 
743   EFI_SUCCESS is returned directly.
744 
745   @param  This              The pointer of EFI_BLOCK_IO_PROTOCOL
746 
747   @retval EFI_SUCCESS       All outstanding data was written to the device
748 
749 **/
750 EFI_STATUS
751 EFIAPI
ScsiDiskFlushBlocks(IN EFI_BLOCK_IO_PROTOCOL * This)752 ScsiDiskFlushBlocks (
753   IN  EFI_BLOCK_IO_PROTOCOL   *This
754   )
755 {
756   //
757   // return directly
758   //
759   return EFI_SUCCESS;
760 }
761 
762 
763 /**
764   Reset SCSI Disk.
765 
766   @param  This                 The pointer of EFI_BLOCK_IO2_PROTOCOL.
767   @param  ExtendedVerification The flag about if extend verificate.
768 
769   @retval EFI_SUCCESS          The device was reset.
770   @retval EFI_DEVICE_ERROR     The device is not functioning properly and could
771                                not be reset.
772   @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().
773 
774 **/
775 EFI_STATUS
776 EFIAPI
ScsiDiskResetEx(IN EFI_BLOCK_IO2_PROTOCOL * This,IN BOOLEAN ExtendedVerification)777 ScsiDiskResetEx (
778   IN  EFI_BLOCK_IO2_PROTOCOL  *This,
779   IN  BOOLEAN                 ExtendedVerification
780   )
781 {
782   EFI_TPL       OldTpl;
783   SCSI_DISK_DEV *ScsiDiskDevice;
784   EFI_STATUS    Status;
785 
786   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
787 
788   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_BLKIO2 (This);
789 
790   Status          = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
791 
792   if (EFI_ERROR (Status)) {
793     Status = EFI_DEVICE_ERROR;
794     goto Done;
795   }
796 
797   if (!ExtendedVerification) {
798     goto Done;
799   }
800 
801   Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
802 
803   if (EFI_ERROR (Status)) {
804     Status = EFI_DEVICE_ERROR;
805     goto Done;
806   }
807 
808 Done:
809   gBS->RestoreTPL (OldTpl);
810   return Status;
811 }
812 
813 /**
814   The function is to Read Block from SCSI Disk.
815 
816   @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL.
817   @param  MediaId    The Id of Media detected.
818   @param  Lba        The logic block address.
819   @param  Token      A pointer to the token associated with the transaction.
820   @param  BufferSize The size of Buffer.
821   @param  Buffer     The buffer to fill the read out data.
822 
823   @retval EFI_SUCCESS           The read request was queued if Token-> Event is
824                                 not NULL. The data was read correctly from the
825                                 device if theToken-> Event is NULL.
826   @retval EFI_DEVICE_ERROR      The device reported an error while attempting
827                                 to perform the read operation.
828   @retval EFI_NO_MEDIA          There is no media in the device.
829   @retval EFI_MEDIA_CHANGED     The MediaId is not for the current media.
830   @retval EFI_BAD_BUFFER_SIZE   The BufferSize parameter is not a multiple of
831                                 the intrinsic block size of the device.
832   @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
833                                 valid, or the buffer is not on proper
834                                 alignment.
835   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
836                                 lack of resources.
837 
838 **/
839 EFI_STATUS
840 EFIAPI
ScsiDiskReadBlocksEx(IN EFI_BLOCK_IO2_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN OUT EFI_BLOCK_IO2_TOKEN * Token,IN UINTN BufferSize,OUT VOID * Buffer)841 ScsiDiskReadBlocksEx (
842   IN     EFI_BLOCK_IO2_PROTOCOL   *This,
843   IN     UINT32                   MediaId,
844   IN     EFI_LBA                  Lba,
845   IN OUT EFI_BLOCK_IO2_TOKEN      *Token,
846   IN     UINTN                    BufferSize,
847   OUT    VOID                     *Buffer
848   )
849 {
850   SCSI_DISK_DEV       *ScsiDiskDevice;
851   EFI_BLOCK_IO_MEDIA  *Media;
852   EFI_STATUS          Status;
853   UINTN               BlockSize;
854   UINTN               NumberOfBlocks;
855   BOOLEAN             MediaChange;
856   EFI_TPL             OldTpl;
857 
858   MediaChange    = FALSE;
859   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);
860   ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);
861 
862   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
863 
864     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);
865     if (EFI_ERROR (Status)) {
866       Status = EFI_DEVICE_ERROR;
867       goto Done;
868     }
869 
870     if (MediaChange) {
871       gBS->ReinstallProtocolInterface (
872             ScsiDiskDevice->Handle,
873             &gEfiBlockIoProtocolGuid,
874             &ScsiDiskDevice->BlkIo,
875             &ScsiDiskDevice->BlkIo
876             );
877       gBS->ReinstallProtocolInterface (
878              ScsiDiskDevice->Handle,
879              &gEfiBlockIo2ProtocolGuid,
880              &ScsiDiskDevice->BlkIo2,
881              &ScsiDiskDevice->BlkIo2
882              );
883       Status = EFI_MEDIA_CHANGED;
884       goto Done;
885     }
886   }
887   //
888   // Get the intrinsic block size
889   //
890   Media           = ScsiDiskDevice->BlkIo2.Media;
891   BlockSize       = Media->BlockSize;
892 
893   NumberOfBlocks  = BufferSize / BlockSize;
894 
895   if (!(Media->MediaPresent)) {
896     Status = EFI_NO_MEDIA;
897     goto Done;
898   }
899 
900   if (MediaId != Media->MediaId) {
901     Status = EFI_MEDIA_CHANGED;
902     goto Done;
903   }
904 
905   if (Buffer == NULL) {
906     Status = EFI_INVALID_PARAMETER;
907     goto Done;
908   }
909 
910   if (BufferSize == 0) {
911     if ((Token != NULL) && (Token->Event != NULL)) {
912       Token->TransactionStatus = EFI_SUCCESS;
913       gBS->SignalEvent (Token->Event);
914     }
915 
916     Status = EFI_SUCCESS;
917     goto Done;
918   }
919 
920   if (BufferSize % BlockSize != 0) {
921     Status = EFI_BAD_BUFFER_SIZE;
922     goto Done;
923   }
924 
925   if (Lba > Media->LastBlock) {
926     Status = EFI_INVALID_PARAMETER;
927     goto Done;
928   }
929 
930   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
931     Status = EFI_INVALID_PARAMETER;
932     goto Done;
933   }
934 
935   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
936     Status = EFI_INVALID_PARAMETER;
937     goto Done;
938   }
939 
940   //
941   // If all the parameters are valid, then perform read sectors command
942   // to transfer data from device to host.
943   //
944   if ((Token != NULL) && (Token->Event != NULL)) {
945     Token->TransactionStatus = EFI_SUCCESS;
946     Status = ScsiDiskAsyncReadSectors (
947                ScsiDiskDevice,
948                Buffer,
949                Lba,
950                NumberOfBlocks,
951                Token
952                );
953   } else {
954     Status = ScsiDiskReadSectors (
955                ScsiDiskDevice,
956                Buffer,
957                Lba,
958                NumberOfBlocks
959                );
960   }
961 
962 Done:
963   gBS->RestoreTPL (OldTpl);
964   return Status;
965 }
966 
967 /**
968   The function is to Write Block to SCSI Disk.
969 
970   @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL.
971   @param  MediaId    The Id of Media detected.
972   @param  Lba        The logic block address.
973   @param  Token      A pointer to the token associated with the transaction.
974   @param  BufferSize The size of Buffer.
975   @param  Buffer     The buffer to fill the read out data.
976 
977   @retval EFI_SUCCESS           The data were written correctly to the device.
978   @retval EFI_WRITE_PROTECTED   The device cannot be written to.
979   @retval EFI_NO_MEDIA          There is no media in the device.
980   @retval EFI_MEDIA_CHANGED     The MediaId is not for the current media.
981   @retval EFI_DEVICE_ERROR      The device reported an error while attempting
982                                 to perform the write operation.
983   @retval EFI_BAD_BUFFER_SIZE   The BufferSize parameter is not a multiple of
984                                 the intrinsic block size of the device.
985   @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not
986                                 valid, or the buffer is not on proper
987                                 alignment.
988 
989 **/
990 EFI_STATUS
991 EFIAPI
ScsiDiskWriteBlocksEx(IN EFI_BLOCK_IO2_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN OUT EFI_BLOCK_IO2_TOKEN * Token,IN UINTN BufferSize,IN VOID * Buffer)992 ScsiDiskWriteBlocksEx (
993   IN     EFI_BLOCK_IO2_PROTOCOL *This,
994   IN     UINT32                 MediaId,
995   IN     EFI_LBA                Lba,
996   IN OUT EFI_BLOCK_IO2_TOKEN    *Token,
997   IN     UINTN                  BufferSize,
998   IN     VOID                   *Buffer
999   )
1000 {
1001   SCSI_DISK_DEV       *ScsiDiskDevice;
1002   EFI_BLOCK_IO_MEDIA  *Media;
1003   EFI_STATUS          Status;
1004   UINTN               BlockSize;
1005   UINTN               NumberOfBlocks;
1006   BOOLEAN             MediaChange;
1007   EFI_TPL             OldTpl;
1008 
1009   MediaChange    = FALSE;
1010   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);
1011   ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);
1012 
1013   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
1014 
1015     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);
1016     if (EFI_ERROR (Status)) {
1017       Status = EFI_DEVICE_ERROR;
1018       goto Done;
1019     }
1020 
1021     if (MediaChange) {
1022       gBS->ReinstallProtocolInterface (
1023             ScsiDiskDevice->Handle,
1024             &gEfiBlockIoProtocolGuid,
1025             &ScsiDiskDevice->BlkIo,
1026             &ScsiDiskDevice->BlkIo
1027             );
1028       gBS->ReinstallProtocolInterface (
1029              ScsiDiskDevice->Handle,
1030              &gEfiBlockIo2ProtocolGuid,
1031              &ScsiDiskDevice->BlkIo2,
1032              &ScsiDiskDevice->BlkIo2
1033              );
1034       Status = EFI_MEDIA_CHANGED;
1035       goto Done;
1036     }
1037   }
1038   //
1039   // Get the intrinsic block size
1040   //
1041   Media           = ScsiDiskDevice->BlkIo2.Media;
1042   BlockSize       = Media->BlockSize;
1043 
1044   NumberOfBlocks  = BufferSize / BlockSize;
1045 
1046   if (!(Media->MediaPresent)) {
1047     Status = EFI_NO_MEDIA;
1048     goto Done;
1049   }
1050 
1051   if (MediaId != Media->MediaId) {
1052     Status = EFI_MEDIA_CHANGED;
1053     goto Done;
1054   }
1055 
1056   if (Media->ReadOnly) {
1057     Status = EFI_WRITE_PROTECTED;
1058     goto Done;
1059   }
1060 
1061   if (BufferSize == 0) {
1062     if ((Token != NULL) && (Token->Event != NULL)) {
1063       Token->TransactionStatus = EFI_SUCCESS;
1064       gBS->SignalEvent (Token->Event);
1065     }
1066 
1067     Status = EFI_SUCCESS;
1068     goto Done;
1069   }
1070 
1071   if (Buffer == NULL) {
1072     Status = EFI_INVALID_PARAMETER;
1073     goto Done;
1074   }
1075 
1076   if (BufferSize % BlockSize != 0) {
1077     Status = EFI_BAD_BUFFER_SIZE;
1078     goto Done;
1079   }
1080 
1081   if (Lba > Media->LastBlock) {
1082     Status = EFI_INVALID_PARAMETER;
1083     goto Done;
1084   }
1085 
1086   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
1087     Status = EFI_INVALID_PARAMETER;
1088     goto Done;
1089   }
1090 
1091   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
1092     Status = EFI_INVALID_PARAMETER;
1093     goto Done;
1094   }
1095 
1096   //
1097   // if all the parameters are valid, then perform write sectors command
1098   // to transfer data from device to host.
1099   //
1100   if ((Token != NULL) && (Token->Event != NULL)) {
1101     Token->TransactionStatus = EFI_SUCCESS;
1102     Status = ScsiDiskAsyncWriteSectors (
1103                ScsiDiskDevice,
1104                Buffer,
1105                Lba,
1106                NumberOfBlocks,
1107                Token
1108                );
1109   } else {
1110     Status = ScsiDiskWriteSectors (
1111                ScsiDiskDevice,
1112                Buffer,
1113                Lba,
1114                NumberOfBlocks
1115                );
1116   }
1117 
1118 Done:
1119   gBS->RestoreTPL (OldTpl);
1120   return Status;
1121 }
1122 
1123 /**
1124   Flush the Block Device.
1125 
1126   @param  This       Indicates a pointer to the calling context.
1127   @param  Token      A pointer to the token associated with the transaction.
1128 
1129   @retval EFI_SUCCESS         All outstanding data was written to the device.
1130   @retval EFI_DEVICE_ERROR    The device reported an error while attempting to
1131                               write data.
1132   @retval EFI_WRITE_PROTECTED The device cannot be written to.
1133   @retval EFI_NO_MEDIA        There is no media in the device.
1134   @retval EFI_MEDIA_CHANGED   The MediaId is not for the current media.
1135 
1136 **/
1137 EFI_STATUS
1138 EFIAPI
ScsiDiskFlushBlocksEx(IN EFI_BLOCK_IO2_PROTOCOL * This,IN OUT EFI_BLOCK_IO2_TOKEN * Token)1139 ScsiDiskFlushBlocksEx (
1140   IN     EFI_BLOCK_IO2_PROTOCOL  *This,
1141   IN OUT EFI_BLOCK_IO2_TOKEN     *Token
1142   )
1143 {
1144   SCSI_DISK_DEV       *ScsiDiskDevice;
1145   EFI_BLOCK_IO_MEDIA  *Media;
1146   EFI_STATUS          Status;
1147   BOOLEAN             MediaChange;
1148   EFI_TPL             OldTpl;
1149 
1150   MediaChange    = FALSE;
1151   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);
1152   ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);
1153 
1154   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
1155 
1156     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);
1157     if (EFI_ERROR (Status)) {
1158       Status = EFI_DEVICE_ERROR;
1159       goto Done;
1160     }
1161 
1162     if (MediaChange) {
1163       gBS->ReinstallProtocolInterface (
1164             ScsiDiskDevice->Handle,
1165             &gEfiBlockIoProtocolGuid,
1166             &ScsiDiskDevice->BlkIo,
1167             &ScsiDiskDevice->BlkIo
1168             );
1169       gBS->ReinstallProtocolInterface (
1170              ScsiDiskDevice->Handle,
1171              &gEfiBlockIo2ProtocolGuid,
1172              &ScsiDiskDevice->BlkIo2,
1173              &ScsiDiskDevice->BlkIo2
1174              );
1175       Status = EFI_MEDIA_CHANGED;
1176       goto Done;
1177     }
1178   }
1179 
1180   Media = ScsiDiskDevice->BlkIo2.Media;
1181 
1182   if (!(Media->MediaPresent)) {
1183     Status = EFI_NO_MEDIA;
1184     goto Done;
1185   }
1186 
1187   if (Media->ReadOnly) {
1188     Status = EFI_WRITE_PROTECTED;
1189     goto Done;
1190   }
1191 
1192   //
1193   // Wait for the BlockIo2 requests queue to become empty
1194   //
1195   while (!IsListEmpty (&ScsiDiskDevice->BlkIo2Queue));
1196 
1197   Status = EFI_SUCCESS;
1198 
1199   //
1200   // Signal caller event
1201   //
1202   if ((Token != NULL) && (Token->Event != NULL)) {
1203     Token->TransactionStatus = EFI_SUCCESS;
1204     gBS->SignalEvent (Token->Event);
1205   }
1206 
1207 Done:
1208   gBS->RestoreTPL (OldTpl);
1209   return Status;
1210 }
1211 
1212 
1213 /**
1214   Detect Device and read out capacity ,if error occurs, parse the sense key.
1215 
1216   @param  ScsiDiskDevice    The pointer of SCSI_DISK_DEV
1217   @param  MustReadCapacity  The flag about reading device capacity
1218   @param  MediaChange       The pointer of flag indicates if media has changed
1219 
1220   @retval EFI_DEVICE_ERROR  Indicates that error occurs
1221   @retval EFI_SUCCESS       Successfully to detect media
1222 
1223 **/
1224 EFI_STATUS
ScsiDiskDetectMedia(IN SCSI_DISK_DEV * ScsiDiskDevice,IN BOOLEAN MustReadCapacity,OUT BOOLEAN * MediaChange)1225 ScsiDiskDetectMedia (
1226   IN   SCSI_DISK_DEV   *ScsiDiskDevice,
1227   IN   BOOLEAN         MustReadCapacity,
1228   OUT  BOOLEAN         *MediaChange
1229   )
1230 {
1231   EFI_STATUS          Status;
1232   EFI_SCSI_SENSE_DATA *SenseData;
1233   UINTN               NumberOfSenseKeys;
1234   BOOLEAN             NeedRetry;
1235   BOOLEAN             NeedReadCapacity;
1236   UINT8               Retry;
1237   UINT8               MaxRetry;
1238   EFI_BLOCK_IO_MEDIA  OldMedia;
1239   UINTN               Action;
1240   EFI_EVENT           TimeoutEvt;
1241 
1242   Status              = EFI_SUCCESS;
1243   SenseData           = NULL;
1244   NumberOfSenseKeys   = 0;
1245   Retry               = 0;
1246   MaxRetry            = 3;
1247   Action              = ACTION_NO_ACTION;
1248   NeedReadCapacity    = FALSE;
1249   *MediaChange        = FALSE;
1250   TimeoutEvt          = NULL;
1251 
1252   CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));
1253 
1254   Status = gBS->CreateEvent (
1255                   EVT_TIMER,
1256                   TPL_CALLBACK,
1257                   NULL,
1258                   NULL,
1259                   &TimeoutEvt
1260                   );
1261   if (EFI_ERROR (Status)) {
1262     return Status;
1263   }
1264 
1265   Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS(120));
1266   if (EFI_ERROR (Status)) {
1267     goto EXIT;
1268   }
1269 
1270   //
1271   // Sending Test_Unit cmd to poll device status.
1272   // If the sense data shows the drive is not ready or reset before, we need poll the device status again.
1273   // We limit the upper boundary to 120 seconds.
1274   //
1275   while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {
1276     Status = ScsiDiskTestUnitReady (
1277               ScsiDiskDevice,
1278               &NeedRetry,
1279               &SenseData,
1280               &NumberOfSenseKeys
1281               );
1282     if (!EFI_ERROR (Status)) {
1283       Status = DetectMediaParsingSenseKeys (
1284                  ScsiDiskDevice,
1285                  SenseData,
1286                  NumberOfSenseKeys,
1287                  &Action
1288                  );
1289       if (EFI_ERROR (Status)) {
1290         goto EXIT;
1291       } else if (Action == ACTION_RETRY_COMMAND_LATER) {
1292         continue;
1293       } else {
1294         break;
1295       }
1296     } else {
1297       Retry++;
1298       if (!NeedRetry || (Retry >= MaxRetry)) {
1299         goto EXIT;
1300       }
1301     }
1302   }
1303 
1304   if (EFI_ERROR (Status)) {
1305     goto EXIT;
1306   }
1307 
1308   //
1309   // ACTION_NO_ACTION: need not read capacity
1310   // other action code: need read capacity
1311   //
1312   if (Action == ACTION_READ_CAPACITY) {
1313     NeedReadCapacity = TRUE;
1314   }
1315 
1316   //
1317   // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,
1318   // retrieve capacity via Read Capacity command
1319   //
1320   if (NeedReadCapacity || MustReadCapacity) {
1321     //
1322     // retrieve media information
1323     //
1324     for (Retry = 0; Retry < MaxRetry; Retry++) {
1325       Status = ScsiDiskReadCapacity (
1326                  ScsiDiskDevice,
1327                  &NeedRetry,
1328                  &SenseData,
1329                  &NumberOfSenseKeys
1330                  );
1331       if (!EFI_ERROR (Status)) {
1332         //
1333         // analyze sense key to action
1334         //
1335         Status = DetectMediaParsingSenseKeys (
1336                    ScsiDiskDevice,
1337                    SenseData,
1338                    NumberOfSenseKeys,
1339                    &Action
1340                    );
1341         if (EFI_ERROR (Status)) {
1342           //
1343           // if Status is error, it may indicate crisis error,
1344           // so return without retry.
1345           //
1346           goto EXIT;
1347         } else if (Action == ACTION_RETRY_COMMAND_LATER) {
1348           Retry = 0;
1349           continue;
1350         } else {
1351           break;
1352         }
1353       } else {
1354         Retry++;
1355         if (!NeedRetry || (Retry >= MaxRetry)) {
1356           goto EXIT;
1357         }
1358       }
1359     }
1360 
1361     if (EFI_ERROR (Status)) {
1362       goto EXIT;
1363     }
1364   }
1365 
1366   if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {
1367     //
1368     // Media change information got from the device
1369     //
1370     *MediaChange = TRUE;
1371   }
1372 
1373   if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {
1374     *MediaChange = TRUE;
1375     ScsiDiskDevice->BlkIo.Media->MediaId += 1;
1376   }
1377 
1378   if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {
1379     *MediaChange = TRUE;
1380     ScsiDiskDevice->BlkIo.Media->MediaId += 1;
1381   }
1382 
1383   if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {
1384     *MediaChange = TRUE;
1385     ScsiDiskDevice->BlkIo.Media->MediaId += 1;
1386   }
1387 
1388   if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {
1389     if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {
1390       //
1391       // when change from no media to media present, reset the MediaId to 1.
1392       //
1393       ScsiDiskDevice->BlkIo.Media->MediaId = 1;
1394     } else {
1395       //
1396       // when no media, reset the MediaId to zero.
1397       //
1398       ScsiDiskDevice->BlkIo.Media->MediaId = 0;
1399     }
1400 
1401     *MediaChange = TRUE;
1402   }
1403 
1404 EXIT:
1405   if (TimeoutEvt != NULL) {
1406     gBS->CloseEvent (TimeoutEvt);
1407   }
1408   return Status;
1409 }
1410 
1411 
1412 /**
1413   Send out Inquiry command to Device.
1414 
1415   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
1416   @param  NeedRetry       Indicates if needs try again when error happens
1417 
1418   @retval  EFI_DEVICE_ERROR  Indicates that error occurs
1419   @retval  EFI_SUCCESS       Successfully to detect media
1420 
1421 **/
1422 EFI_STATUS
ScsiDiskInquiryDevice(IN OUT SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry)1423 ScsiDiskInquiryDevice (
1424   IN OUT  SCSI_DISK_DEV   *ScsiDiskDevice,
1425      OUT  BOOLEAN         *NeedRetry
1426   )
1427 {
1428   UINT32                                InquiryDataLength;
1429   UINT8                                 SenseDataLength;
1430   UINT8                                 HostAdapterStatus;
1431   UINT8                                 TargetStatus;
1432   EFI_SCSI_SENSE_DATA                   *SenseDataArray;
1433   UINTN                                 NumberOfSenseKeys;
1434   EFI_STATUS                            Status;
1435   UINT8                                 MaxRetry;
1436   UINT8                                 Index;
1437   EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE *SupportedVpdPages;
1438   EFI_SCSI_BLOCK_LIMITS_VPD_PAGE        *BlockLimits;
1439   UINTN                                 PageLength;
1440 
1441   InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);
1442   SenseDataLength   = 0;
1443 
1444   Status = ScsiInquiryCommand (
1445             ScsiDiskDevice->ScsiIo,
1446             SCSI_DISK_TIMEOUT,
1447             NULL,
1448             &SenseDataLength,
1449             &HostAdapterStatus,
1450             &TargetStatus,
1451             (VOID *) &(ScsiDiskDevice->InquiryData),
1452             &InquiryDataLength,
1453             FALSE
1454             );
1455     //
1456     // no need to check HostAdapterStatus and TargetStatus
1457     //
1458   if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {
1459     ParseInquiryData (ScsiDiskDevice);
1460 
1461     if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {
1462       //
1463       // Check whether the device supports Block Limits VPD page (0xB0)
1464       //
1465       SupportedVpdPages = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
1466       if (SupportedVpdPages == NULL) {
1467         *NeedRetry = FALSE;
1468         return EFI_DEVICE_ERROR;
1469       }
1470       ZeroMem (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
1471       InquiryDataLength = sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE);
1472       SenseDataLength   = 0;
1473       Status = ScsiInquiryCommandEx (
1474                  ScsiDiskDevice->ScsiIo,
1475                  SCSI_DISK_TIMEOUT,
1476                  NULL,
1477                  &SenseDataLength,
1478                  &HostAdapterStatus,
1479                  &TargetStatus,
1480                  (VOID *) SupportedVpdPages,
1481                  &InquiryDataLength,
1482                  TRUE,
1483                  EFI_SCSI_PAGE_CODE_SUPPORTED_VPD
1484                  );
1485       if (!EFI_ERROR (Status)) {
1486         PageLength = (SupportedVpdPages->PageLength2 << 8)
1487                    |  SupportedVpdPages->PageLength1;
1488         for (Index = 0; Index < PageLength; Index++) {
1489           if (SupportedVpdPages->SupportedVpdPageList[Index] == EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD) {
1490             break;
1491           }
1492         }
1493 
1494         //
1495         // Query the Block Limits VPD page
1496         //
1497         if (Index < PageLength) {
1498           BlockLimits = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));
1499           if (BlockLimits == NULL) {
1500             FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
1501             *NeedRetry = FALSE;
1502             return EFI_DEVICE_ERROR;
1503           }
1504           ZeroMem (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));
1505           InquiryDataLength = sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE);
1506           SenseDataLength   = 0;
1507           Status = ScsiInquiryCommandEx (
1508                      ScsiDiskDevice->ScsiIo,
1509                      SCSI_DISK_TIMEOUT,
1510                      NULL,
1511                      &SenseDataLength,
1512                      &HostAdapterStatus,
1513                      &TargetStatus,
1514                      (VOID *) BlockLimits,
1515                      &InquiryDataLength,
1516                      TRUE,
1517                      EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD
1518                      );
1519           if (!EFI_ERROR (Status)) {
1520             ScsiDiskDevice->BlkIo.Media->OptimalTransferLengthGranularity =
1521               (BlockLimits->OptimalTransferLengthGranularity2 << 8) |
1522                BlockLimits->OptimalTransferLengthGranularity1;
1523           }
1524 
1525           FreeAlignedBuffer (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));
1526         }
1527       }
1528 
1529       FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
1530     }
1531   }
1532 
1533   if (!EFI_ERROR (Status)) {
1534     return EFI_SUCCESS;
1535 
1536   } else if (Status == EFI_NOT_READY) {
1537     *NeedRetry = TRUE;
1538     return EFI_DEVICE_ERROR;
1539 
1540   } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {
1541     *NeedRetry = FALSE;
1542     return EFI_DEVICE_ERROR;
1543   }
1544   //
1545   // go ahead to check HostAdapterStatus and TargetStatus
1546   // (EFI_TIMEOUT, EFI_DEVICE_ERROR)
1547   //
1548 
1549   Status = CheckHostAdapterStatus (HostAdapterStatus);
1550   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
1551     *NeedRetry = TRUE;
1552     return EFI_DEVICE_ERROR;
1553   } else if (Status == EFI_DEVICE_ERROR) {
1554       //
1555       // reset the scsi channel
1556       //
1557     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
1558     *NeedRetry = FALSE;
1559     return EFI_DEVICE_ERROR;
1560   }
1561 
1562   Status = CheckTargetStatus (TargetStatus);
1563   if (Status == EFI_NOT_READY) {
1564     //
1565     // reset the scsi device
1566     //
1567     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
1568     *NeedRetry = TRUE;
1569     return EFI_DEVICE_ERROR;
1570 
1571   } else if (Status == EFI_DEVICE_ERROR) {
1572     *NeedRetry = FALSE;
1573     return EFI_DEVICE_ERROR;
1574   }
1575 
1576   //
1577   // if goes here, meant ScsiInquiryCommand() failed.
1578   // if ScsiDiskRequestSenseKeys() succeeds at last,
1579   // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)
1580   //
1581   MaxRetry = 3;
1582   for (Index = 0; Index < MaxRetry; Index++) {
1583     Status = ScsiDiskRequestSenseKeys (
1584               ScsiDiskDevice,
1585               NeedRetry,
1586               &SenseDataArray,
1587               &NumberOfSenseKeys,
1588               TRUE
1589               );
1590     if (!EFI_ERROR (Status)) {
1591       *NeedRetry = TRUE;
1592       return EFI_DEVICE_ERROR;
1593     }
1594 
1595     if (!*NeedRetry) {
1596       return EFI_DEVICE_ERROR;
1597     }
1598   }
1599   //
1600   // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
1601   // set *NeedRetry = FALSE to avoid the outside caller try again.
1602   //
1603   *NeedRetry = FALSE;
1604   return EFI_DEVICE_ERROR;
1605 }
1606 
1607 /**
1608   To test device.
1609 
1610   When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;
1611   When Test Unit Ready command encounters any error caused by host adapter or
1612   target, return error without retrieving Sense Keys.
1613 
1614   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
1615   @param  NeedRetry          The pointer of flag indicates try again
1616   @param  SenseDataArray     The pointer of an array of sense data
1617   @param  NumberOfSenseKeys  The pointer of the number of sense data array
1618 
1619   @retval EFI_DEVICE_ERROR   Indicates that error occurs
1620   @retval EFI_SUCCESS        Successfully to test unit
1621 
1622 **/
1623 EFI_STATUS
ScsiDiskTestUnitReady(IN SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry,OUT EFI_SCSI_SENSE_DATA ** SenseDataArray,OUT UINTN * NumberOfSenseKeys)1624 ScsiDiskTestUnitReady (
1625   IN  SCSI_DISK_DEV         *ScsiDiskDevice,
1626   OUT BOOLEAN               *NeedRetry,
1627   OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,
1628   OUT UINTN                 *NumberOfSenseKeys
1629   )
1630 {
1631   EFI_STATUS  Status;
1632   UINT8       SenseDataLength;
1633   UINT8       HostAdapterStatus;
1634   UINT8       TargetStatus;
1635   UINT8       Index;
1636   UINT8       MaxRetry;
1637 
1638   SenseDataLength     = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
1639   *NumberOfSenseKeys  = 0;
1640 
1641   //
1642   // Parameter 3 and 4: do not require sense data, retrieve it when needed.
1643   //
1644   Status = ScsiTestUnitReadyCommand (
1645             ScsiDiskDevice->ScsiIo,
1646             SCSI_DISK_TIMEOUT,
1647             ScsiDiskDevice->SenseData,
1648             &SenseDataLength,
1649             &HostAdapterStatus,
1650             &TargetStatus
1651             );
1652   //
1653   // no need to check HostAdapterStatus and TargetStatus
1654   //
1655   if (Status == EFI_NOT_READY) {
1656     *NeedRetry = TRUE;
1657     return EFI_DEVICE_ERROR;
1658 
1659   } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {
1660     *NeedRetry = FALSE;
1661     return EFI_DEVICE_ERROR;
1662   }
1663   //
1664   // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)
1665   //
1666 
1667   Status = CheckHostAdapterStatus (HostAdapterStatus);
1668   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
1669     *NeedRetry = TRUE;
1670     return EFI_DEVICE_ERROR;
1671 
1672   } else if (Status == EFI_DEVICE_ERROR) {
1673     //
1674     // reset the scsi channel
1675     //
1676     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
1677     *NeedRetry = FALSE;
1678     return EFI_DEVICE_ERROR;
1679   }
1680 
1681   Status = CheckTargetStatus (TargetStatus);
1682   if (Status == EFI_NOT_READY) {
1683     //
1684     // reset the scsi device
1685     //
1686     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
1687     *NeedRetry = TRUE;
1688     return EFI_DEVICE_ERROR;
1689 
1690   } else if (Status == EFI_DEVICE_ERROR) {
1691     *NeedRetry = FALSE;
1692     return EFI_DEVICE_ERROR;
1693   }
1694 
1695   if (SenseDataLength != 0) {
1696     *NumberOfSenseKeys = SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA);
1697     *SenseDataArray    = ScsiDiskDevice->SenseData;
1698     return EFI_SUCCESS;
1699   }
1700 
1701   MaxRetry = 3;
1702   for (Index = 0; Index < MaxRetry; Index++) {
1703     Status = ScsiDiskRequestSenseKeys (
1704               ScsiDiskDevice,
1705               NeedRetry,
1706               SenseDataArray,
1707               NumberOfSenseKeys,
1708               FALSE
1709               );
1710     if (!EFI_ERROR (Status)) {
1711       return EFI_SUCCESS;
1712     }
1713 
1714     if (!*NeedRetry) {
1715       return EFI_DEVICE_ERROR;
1716     }
1717   }
1718   //
1719   // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
1720   // set *NeedRetry = FALSE to avoid the outside caller try again.
1721   //
1722   *NeedRetry = FALSE;
1723   return EFI_DEVICE_ERROR;
1724 }
1725 
1726 /**
1727   Parsing Sense Keys which got from request sense command.
1728 
1729   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
1730   @param  SenseData          The pointer of EFI_SCSI_SENSE_DATA
1731   @param  NumberOfSenseKeys  The number of sense key
1732   @param  Action             The pointer of action which indicates what is need to do next
1733 
1734   @retval EFI_DEVICE_ERROR   Indicates that error occurs
1735   @retval EFI_SUCCESS        Successfully to complete the parsing
1736 
1737 **/
1738 EFI_STATUS
DetectMediaParsingSenseKeys(OUT SCSI_DISK_DEV * ScsiDiskDevice,IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN NumberOfSenseKeys,OUT UINTN * Action)1739 DetectMediaParsingSenseKeys (
1740   OUT  SCSI_DISK_DEV           *ScsiDiskDevice,
1741   IN   EFI_SCSI_SENSE_DATA     *SenseData,
1742   IN   UINTN                   NumberOfSenseKeys,
1743   OUT  UINTN                   *Action
1744   )
1745 {
1746   BOOLEAN RetryLater;
1747 
1748   //
1749   // Default is to read capacity, unless..
1750   //
1751   *Action = ACTION_READ_CAPACITY;
1752 
1753   if (NumberOfSenseKeys == 0) {
1754     if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {
1755       *Action = ACTION_NO_ACTION;
1756     }
1757     return EFI_SUCCESS;
1758   }
1759 
1760   if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {
1761     //
1762     // No Sense Key returned from last submitted command
1763     //
1764     if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {
1765       *Action = ACTION_NO_ACTION;
1766     }
1767     return EFI_SUCCESS;
1768   }
1769 
1770   if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {
1771     ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
1772     ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;
1773     *Action = ACTION_NO_ACTION;
1774     DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n"));
1775     return EFI_SUCCESS;
1776   }
1777 
1778   if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {
1779     ScsiDiskDevice->BlkIo.Media->MediaId++;
1780     DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n"));
1781     return EFI_SUCCESS;
1782   }
1783 
1784   if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {
1785     *Action = ACTION_RETRY_COMMAND_LATER;
1786     DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n"));
1787     return EFI_SUCCESS;
1788   }
1789 
1790   if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
1791     DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaError\n"));
1792     *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
1793     return EFI_DEVICE_ERROR;
1794   }
1795 
1796   if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
1797     DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsHardwareError\n"));
1798     *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
1799     return EFI_DEVICE_ERROR;
1800   }
1801 
1802   if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {
1803     if (RetryLater) {
1804       *Action = ACTION_RETRY_COMMAND_LATER;
1805       DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n"));
1806       return EFI_SUCCESS;
1807     }
1808     *Action = ACTION_NO_ACTION;
1809     return EFI_DEVICE_ERROR;
1810   }
1811 
1812   *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
1813   DEBUG ((EFI_D_VERBOSE, "ScsiDisk: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code));
1814   return EFI_SUCCESS;
1815 }
1816 
1817 
1818 /**
1819   Send read capacity command to device and get the device parameter.
1820 
1821   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
1822   @param  NeedRetry          The pointer of flag indicates if need a retry
1823   @param  SenseDataArray     The pointer of an array of sense data
1824   @param  NumberOfSenseKeys  The number of sense key
1825 
1826   @retval EFI_DEVICE_ERROR   Indicates that error occurs
1827   @retval EFI_SUCCESS        Successfully to read capacity or sense data is received.
1828 
1829 **/
1830 EFI_STATUS
ScsiDiskReadCapacity(IN OUT SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry,OUT EFI_SCSI_SENSE_DATA ** SenseDataArray,OUT UINTN * NumberOfSenseKeys)1831 ScsiDiskReadCapacity (
1832   IN  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,
1833       OUT  BOOLEAN                 *NeedRetry,
1834       OUT  EFI_SCSI_SENSE_DATA     **SenseDataArray,
1835       OUT  UINTN                   *NumberOfSenseKeys
1836   )
1837 {
1838   UINT8                         HostAdapterStatus;
1839   UINT8                         TargetStatus;
1840   EFI_STATUS                    CommandStatus;
1841   EFI_STATUS                    Status;
1842   UINT8                         Index;
1843   UINT8                         MaxRetry;
1844   UINT8                         SenseDataLength;
1845   UINT32                        DataLength10;
1846   UINT32                        DataLength16;
1847   EFI_SCSI_DISK_CAPACITY_DATA   *CapacityData10;
1848   EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16;
1849 
1850   CapacityData10 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
1851   if (CapacityData10 == NULL) {
1852     *NeedRetry = FALSE;
1853     return EFI_DEVICE_ERROR;
1854   }
1855   CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
1856   if (CapacityData16 == NULL) {
1857     FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
1858     *NeedRetry = FALSE;
1859     return EFI_DEVICE_ERROR;
1860   }
1861 
1862   SenseDataLength       = 0;
1863   DataLength10          = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);
1864   DataLength16          = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);
1865   ZeroMem (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
1866   ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
1867 
1868   *NumberOfSenseKeys  = 0;
1869   *NeedRetry          = FALSE;
1870 
1871   //
1872   // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh,
1873   // 16 byte command should be used to access large hard disk >2TB
1874   //
1875   CommandStatus = ScsiReadCapacityCommand (
1876                     ScsiDiskDevice->ScsiIo,
1877                     SCSI_DISK_TIMEOUT,
1878                     NULL,
1879                     &SenseDataLength,
1880                     &HostAdapterStatus,
1881                     &TargetStatus,
1882                     (VOID *) CapacityData10,
1883                     &DataLength10,
1884                     FALSE
1885                     );
1886 
1887   ScsiDiskDevice->Cdb16Byte = FALSE;
1888   if ((!EFI_ERROR (CommandStatus)) && (CapacityData10->LastLba3 == 0xff) && (CapacityData10->LastLba2 == 0xff) &&
1889       (CapacityData10->LastLba1 == 0xff) && (CapacityData10->LastLba0 == 0xff)) {
1890     //
1891     // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB
1892     //
1893     ScsiDiskDevice->Cdb16Byte = TRUE;
1894     //
1895     // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock
1896     // and LowestAlignedLba
1897     //
1898     CommandStatus = ScsiReadCapacity16Command (
1899                       ScsiDiskDevice->ScsiIo,
1900                       SCSI_DISK_TIMEOUT,
1901                       NULL,
1902                       &SenseDataLength,
1903                       &HostAdapterStatus,
1904                       &TargetStatus,
1905                       (VOID *) CapacityData16,
1906                       &DataLength16,
1907                       FALSE
1908                       );
1909   }
1910 
1911     //
1912     // no need to check HostAdapterStatus and TargetStatus
1913     //
1914    if (CommandStatus == EFI_SUCCESS) {
1915      GetMediaInfo (ScsiDiskDevice, CapacityData10, CapacityData16);
1916      FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
1917      FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
1918      return EFI_SUCCESS;
1919    }
1920 
1921    FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
1922    FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
1923 
1924    if (CommandStatus == EFI_NOT_READY) {
1925      *NeedRetry = TRUE;
1926      return EFI_DEVICE_ERROR;
1927    } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {
1928      *NeedRetry = FALSE;
1929      return EFI_DEVICE_ERROR;
1930    }
1931 
1932    //
1933    // go ahead to check HostAdapterStatus and TargetStatus
1934    // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
1935    //
1936 
1937    Status = CheckHostAdapterStatus (HostAdapterStatus);
1938    if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
1939      *NeedRetry = TRUE;
1940      return EFI_DEVICE_ERROR;
1941 
1942    } else if (Status == EFI_DEVICE_ERROR) {
1943     //
1944     // reset the scsi channel
1945     //
1946     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
1947     *NeedRetry = FALSE;
1948     return EFI_DEVICE_ERROR;
1949   }
1950 
1951   Status = CheckTargetStatus (TargetStatus);
1952   if (Status == EFI_NOT_READY) {
1953     //
1954     // reset the scsi device
1955     //
1956     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
1957     *NeedRetry = TRUE;
1958     return EFI_DEVICE_ERROR;
1959 
1960   } else if (Status == EFI_DEVICE_ERROR) {
1961     *NeedRetry = FALSE;
1962     return EFI_DEVICE_ERROR;
1963   }
1964 
1965   //
1966   // if goes here, meant ScsiReadCapacityCommand() failed.
1967   // if ScsiDiskRequestSenseKeys() succeeds at last,
1968   // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)
1969   //
1970   MaxRetry = 3;
1971   for (Index = 0; Index < MaxRetry; Index++) {
1972 
1973     Status = ScsiDiskRequestSenseKeys (
1974               ScsiDiskDevice,
1975               NeedRetry,
1976               SenseDataArray,
1977               NumberOfSenseKeys,
1978               TRUE
1979               );
1980     if (!EFI_ERROR (Status)) {
1981       return EFI_SUCCESS;
1982     }
1983 
1984     if (!*NeedRetry) {
1985       return EFI_DEVICE_ERROR;
1986     }
1987   }
1988   //
1989   // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
1990   // set *NeedRetry = FALSE to avoid the outside caller try again.
1991   //
1992   *NeedRetry = FALSE;
1993   return EFI_DEVICE_ERROR;
1994 }
1995 
1996 /**
1997   Check the HostAdapter status and re-interpret it in EFI_STATUS.
1998 
1999   @param  HostAdapterStatus  Host Adapter status
2000 
2001   @retval  EFI_SUCCESS       Host adapter is OK.
2002   @retval  EFI_TIMEOUT       Timeout.
2003   @retval  EFI_NOT_READY     Adapter NOT ready.
2004   @retval  EFI_DEVICE_ERROR  Adapter device error.
2005 
2006 **/
2007 EFI_STATUS
CheckHostAdapterStatus(IN UINT8 HostAdapterStatus)2008 CheckHostAdapterStatus (
2009   IN UINT8   HostAdapterStatus
2010   )
2011 {
2012   switch (HostAdapterStatus) {
2013   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:
2014     return EFI_SUCCESS;
2015 
2016   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:
2017   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:
2018   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:
2019     return EFI_TIMEOUT;
2020 
2021   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:
2022   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:
2023   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:
2024   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:
2025   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:
2026     return EFI_NOT_READY;
2027 
2028   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:
2029   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:
2030     return EFI_DEVICE_ERROR;
2031 
2032   default:
2033     return EFI_SUCCESS;
2034   }
2035 }
2036 
2037 
2038 /**
2039   Check the target status and re-interpret it in EFI_STATUS.
2040 
2041   @param  TargetStatus  Target status
2042 
2043   @retval EFI_NOT_READY       Device is NOT ready.
2044   @retval EFI_DEVICE_ERROR
2045   @retval EFI_SUCCESS
2046 
2047 **/
2048 EFI_STATUS
CheckTargetStatus(IN UINT8 TargetStatus)2049 CheckTargetStatus (
2050   IN  UINT8   TargetStatus
2051   )
2052 {
2053   switch (TargetStatus) {
2054   case EFI_EXT_SCSI_STATUS_TARGET_GOOD:
2055   case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:
2056   case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:
2057     return EFI_SUCCESS;
2058 
2059   case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:
2060   case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:
2061   case EFI_EXT_SCSI_STATUS_TARGET_BUSY:
2062   case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:
2063     return EFI_NOT_READY;
2064 
2065   case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:
2066     return EFI_DEVICE_ERROR;
2067 
2068   default:
2069     return EFI_SUCCESS;
2070   }
2071 }
2072 
2073 
2074 /**
2075   Retrieve all sense keys from the device.
2076 
2077   When encountering error during the process, if retrieve sense keys before
2078   error encountered, it returns the sense keys with return status set to EFI_SUCCESS,
2079   and NeedRetry set to FALSE; otherwize, return the proper return status.
2080 
2081   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
2082   @param  NeedRetry          The pointer of flag indicates if need a retry
2083   @param  SenseDataArray     The pointer of an array of sense data
2084   @param  NumberOfSenseKeys  The number of sense key
2085   @param  AskResetIfError    The flag indicates if need reset when error occurs
2086 
2087   @retval EFI_DEVICE_ERROR   Indicates that error occurs
2088   @retval EFI_SUCCESS        Successfully to request sense key
2089 
2090 **/
2091 EFI_STATUS
ScsiDiskRequestSenseKeys(IN OUT SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry,OUT EFI_SCSI_SENSE_DATA ** SenseDataArray,OUT UINTN * NumberOfSenseKeys,IN BOOLEAN AskResetIfError)2092 ScsiDiskRequestSenseKeys (
2093   IN  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,
2094       OUT  BOOLEAN                 *NeedRetry,
2095       OUT  EFI_SCSI_SENSE_DATA     **SenseDataArray,
2096       OUT  UINTN                   *NumberOfSenseKeys,
2097   IN       BOOLEAN                 AskResetIfError
2098   )
2099 {
2100   EFI_SCSI_SENSE_DATA *PtrSenseData;
2101   UINT8               SenseDataLength;
2102   BOOLEAN             SenseReq;
2103   EFI_STATUS          Status;
2104   EFI_STATUS          FallStatus;
2105   UINT8               HostAdapterStatus;
2106   UINT8               TargetStatus;
2107 
2108   FallStatus      = EFI_SUCCESS;
2109   SenseDataLength = (UINT8) sizeof (EFI_SCSI_SENSE_DATA);
2110 
2111   ZeroMem (
2112     ScsiDiskDevice->SenseData,
2113     sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)
2114     );
2115 
2116   *NumberOfSenseKeys  = 0;
2117   *SenseDataArray     = ScsiDiskDevice->SenseData;
2118   Status              = EFI_SUCCESS;
2119   PtrSenseData        = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SENSE_DATA));
2120   if (PtrSenseData == NULL) {
2121     return EFI_DEVICE_ERROR;
2122   }
2123 
2124   for (SenseReq = TRUE; SenseReq;) {
2125     ZeroMem (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));
2126     Status = ScsiRequestSenseCommand (
2127               ScsiDiskDevice->ScsiIo,
2128               SCSI_DISK_TIMEOUT,
2129               PtrSenseData,
2130               &SenseDataLength,
2131               &HostAdapterStatus,
2132               &TargetStatus
2133               );
2134      if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {
2135         FallStatus = EFI_SUCCESS;
2136 
2137      } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
2138        *NeedRetry  = TRUE;
2139        FallStatus  = EFI_DEVICE_ERROR;
2140 
2141      } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {
2142        *NeedRetry  = FALSE;
2143        FallStatus  = EFI_DEVICE_ERROR;
2144 
2145      } else if (Status == EFI_DEVICE_ERROR) {
2146         if (AskResetIfError) {
2147           ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
2148         }
2149 
2150         FallStatus = EFI_DEVICE_ERROR;
2151     }
2152 
2153     if (EFI_ERROR (FallStatus)) {
2154       if (*NumberOfSenseKeys != 0) {
2155         *NeedRetry = FALSE;
2156         Status = EFI_SUCCESS;
2157         goto EXIT;
2158       } else {
2159         Status = EFI_DEVICE_ERROR;
2160         goto EXIT;
2161       }
2162     }
2163 
2164     CopyMem (ScsiDiskDevice->SenseData + *NumberOfSenseKeys, PtrSenseData, SenseDataLength);
2165     (*NumberOfSenseKeys) += 1;
2166 
2167     //
2168     // no more sense key or number of sense keys exceeds predefined,
2169     // skip the loop.
2170     //
2171     if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) ||
2172         (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {
2173       SenseReq = FALSE;
2174     }
2175   }
2176 
2177 EXIT:
2178   FreeAlignedBuffer (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));
2179   return Status;
2180 }
2181 
2182 
2183 /**
2184   Get information from media read capacity command.
2185 
2186   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
2187   @param  Capacity10      The pointer of EFI_SCSI_DISK_CAPACITY_DATA
2188   @param  Capacity16      The pointer of EFI_SCSI_DISK_CAPACITY_DATA16
2189 
2190 **/
2191 VOID
GetMediaInfo(IN OUT SCSI_DISK_DEV * ScsiDiskDevice,IN EFI_SCSI_DISK_CAPACITY_DATA * Capacity10,IN EFI_SCSI_DISK_CAPACITY_DATA16 * Capacity16)2192 GetMediaInfo (
2193   IN OUT SCSI_DISK_DEV                  *ScsiDiskDevice,
2194   IN     EFI_SCSI_DISK_CAPACITY_DATA    *Capacity10,
2195   IN     EFI_SCSI_DISK_CAPACITY_DATA16  *Capacity16
2196   )
2197 {
2198   UINT8       *Ptr;
2199 
2200   if (!ScsiDiskDevice->Cdb16Byte) {
2201     ScsiDiskDevice->BlkIo.Media->LastBlock =  (Capacity10->LastLba3 << 24) |
2202                                               (Capacity10->LastLba2 << 16) |
2203                                               (Capacity10->LastLba1 << 8)  |
2204                                                Capacity10->LastLba0;
2205 
2206     ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |
2207                                              (Capacity10->BlockSize2 << 16) |
2208                                              (Capacity10->BlockSize1 << 8)  |
2209                                               Capacity10->BlockSize0;
2210     ScsiDiskDevice->BlkIo.Media->LowestAlignedLba               = 0;
2211     ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock  = 0;
2212   } else {
2213     Ptr = (UINT8*)&ScsiDiskDevice->BlkIo.Media->LastBlock;
2214     *Ptr++ = Capacity16->LastLba0;
2215     *Ptr++ = Capacity16->LastLba1;
2216     *Ptr++ = Capacity16->LastLba2;
2217     *Ptr++ = Capacity16->LastLba3;
2218     *Ptr++ = Capacity16->LastLba4;
2219     *Ptr++ = Capacity16->LastLba5;
2220     *Ptr++ = Capacity16->LastLba6;
2221     *Ptr   = Capacity16->LastLba7;
2222 
2223     ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |
2224                                              (Capacity16->BlockSize2 << 16) |
2225                                              (Capacity16->BlockSize1 << 8)  |
2226                                               Capacity16->BlockSize0;
2227 
2228     ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8) |
2229                                                      Capacity16->LowestAlignLogic1;
2230     ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock  = (1 << Capacity16->LogicPerPhysical);
2231   }
2232 
2233   ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;
2234 }
2235 
2236 /**
2237   Parse Inquiry data.
2238 
2239   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
2240 
2241 **/
2242 VOID
ParseInquiryData(IN OUT SCSI_DISK_DEV * ScsiDiskDevice)2243 ParseInquiryData (
2244   IN OUT SCSI_DISK_DEV   *ScsiDiskDevice
2245   )
2246 {
2247   ScsiDiskDevice->FixedDevice               = (BOOLEAN) ((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);
2248   ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);
2249 }
2250 
2251 /**
2252   Read sector from SCSI Disk.
2253 
2254   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
2255   @param  Buffer          The buffer to fill in the read out data
2256   @param  Lba             Logic block address
2257   @param  NumberOfBlocks  The number of blocks to read
2258 
2259   @retval EFI_DEVICE_ERROR  Indicates a device error.
2260   @retval EFI_SUCCESS       Operation is successful.
2261 
2262 **/
2263 EFI_STATUS
ScsiDiskReadSectors(IN SCSI_DISK_DEV * ScsiDiskDevice,OUT VOID * Buffer,IN EFI_LBA Lba,IN UINTN NumberOfBlocks)2264 ScsiDiskReadSectors (
2265   IN   SCSI_DISK_DEV     *ScsiDiskDevice,
2266   OUT  VOID              *Buffer,
2267   IN   EFI_LBA           Lba,
2268   IN   UINTN             NumberOfBlocks
2269   )
2270 {
2271   UINTN               BlocksRemaining;
2272   UINT8               *PtrBuffer;
2273   UINT32              BlockSize;
2274   UINT32              ByteCount;
2275   UINT32              MaxBlock;
2276   UINT32              SectorCount;
2277   UINT32              NextSectorCount;
2278   UINT64              Timeout;
2279   EFI_STATUS          Status;
2280   UINT8               Index;
2281   UINT8               MaxRetry;
2282   BOOLEAN             NeedRetry;
2283 
2284   Status            = EFI_SUCCESS;
2285 
2286   BlocksRemaining   = NumberOfBlocks;
2287   BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;
2288 
2289   //
2290   // limit the data bytes that can be transferred by one Read(10) or Read(16) Command
2291   //
2292   if (!ScsiDiskDevice->Cdb16Byte) {
2293     MaxBlock         = 0xFFFF;
2294   } else {
2295     MaxBlock         = 0xFFFFFFFF;
2296   }
2297 
2298   PtrBuffer = Buffer;
2299 
2300   while (BlocksRemaining > 0) {
2301 
2302     if (BlocksRemaining <= MaxBlock) {
2303       if (!ScsiDiskDevice->Cdb16Byte) {
2304         SectorCount = (UINT16) BlocksRemaining;
2305       } else {
2306         SectorCount = (UINT32) BlocksRemaining;
2307       }
2308     } else {
2309       SectorCount = MaxBlock;
2310     }
2311 
2312     ByteCount = SectorCount * BlockSize;
2313     //
2314     // |------------------------|-----------------|------------------|-----------------|
2315     // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |
2316     // |------------------------|-----------------|------------------|-----------------|
2317     // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |
2318     // |------------------------|-----------------|------------------|-----------------|
2319     // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |
2320     // |------------------------|-----------------|------------------|-----------------|
2321     // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |
2322     // |------------------------|-----------------|------------------|-----------------|
2323     // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |
2324     // |------------------------|-----------------|------------------|-----------------|
2325     // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |
2326     // |------------------------|-----------------|------------------|-----------------|
2327     // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |
2328     // |------------------------|-----------------|------------------|-----------------|
2329     // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |
2330     // |------------------------|-----------------|------------------|-----------------|
2331     // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |
2332     // |------------------------|-----------------|------------------|-----------------|
2333     // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |
2334     // |------------------------|-----------------|------------------|-----------------|
2335     // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |
2336     // |------------------------|-----------------|------------------|-----------------|
2337     //
2338     // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use
2339     // the lowest transfer rate to calculate the possible maximum timeout value for each operation.
2340     // From the above table, we could know 2.1Mbytes per second is lowest one.
2341     // The timout value is rounded up to nearest integar and here an additional 30s is added
2342     // to follow ATA spec in which it mentioned that the device may take up to 30s to respond
2343     // commands in the Standby/Idle mode.
2344     //
2345     Timeout   = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);
2346 
2347     MaxRetry  = 2;
2348     for (Index = 0; Index < MaxRetry; Index++) {
2349       if (!ScsiDiskDevice->Cdb16Byte) {
2350         Status = ScsiDiskRead10 (
2351                   ScsiDiskDevice,
2352                   &NeedRetry,
2353                   Timeout,
2354                   PtrBuffer,
2355                   &ByteCount,
2356                   (UINT32) Lba,
2357                   SectorCount
2358                   );
2359       } else {
2360         Status = ScsiDiskRead16 (
2361                   ScsiDiskDevice,
2362                   &NeedRetry,
2363                   Timeout,
2364                   PtrBuffer,
2365                   &ByteCount,
2366                   Lba,
2367                   SectorCount
2368                   );
2369       }
2370       if (!EFI_ERROR (Status)) {
2371         break;
2372       }
2373 
2374       if (!NeedRetry) {
2375         return EFI_DEVICE_ERROR;
2376       }
2377 
2378       //
2379       // We need to retry. However, if ScsiDiskRead10() or ScsiDiskRead16() has
2380       // lowered ByteCount on output, we must make sure that we lower
2381       // SectorCount accordingly. SectorCount will be encoded in the CDB, and
2382       // it is invalid to request more sectors in the CDB than the entire
2383       // transfer (ie. ByteCount) can carry.
2384       //
2385       // In addition, ByteCount is only expected to go down, or stay unchaged.
2386       // Therefore we don't need to update Timeout: the original timeout should
2387       // accommodate shorter transfers too.
2388       //
2389       NextSectorCount = ByteCount / BlockSize;
2390       if (NextSectorCount < SectorCount) {
2391         SectorCount = NextSectorCount;
2392         //
2393         // Account for any rounding down.
2394         //
2395         ByteCount = SectorCount * BlockSize;
2396       }
2397     }
2398 
2399     if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {
2400       return EFI_DEVICE_ERROR;
2401     }
2402 
2403     //
2404     // actual transferred sectors
2405     //
2406     SectorCount = ByteCount / BlockSize;
2407 
2408     Lba += SectorCount;
2409     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
2410     BlocksRemaining -= SectorCount;
2411   }
2412 
2413   return EFI_SUCCESS;
2414 }
2415 
2416 /**
2417   Write sector to SCSI Disk.
2418 
2419   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
2420   @param  Buffer          The buffer of data to be written into SCSI Disk
2421   @param  Lba             Logic block address
2422   @param  NumberOfBlocks  The number of blocks to read
2423 
2424   @retval EFI_DEVICE_ERROR  Indicates a device error.
2425   @retval EFI_SUCCESS       Operation is successful.
2426 
2427 **/
2428 EFI_STATUS
ScsiDiskWriteSectors(IN SCSI_DISK_DEV * ScsiDiskDevice,IN VOID * Buffer,IN EFI_LBA Lba,IN UINTN NumberOfBlocks)2429 ScsiDiskWriteSectors (
2430   IN  SCSI_DISK_DEV     *ScsiDiskDevice,
2431   IN  VOID              *Buffer,
2432   IN  EFI_LBA           Lba,
2433   IN  UINTN             NumberOfBlocks
2434   )
2435 {
2436   UINTN               BlocksRemaining;
2437   UINT8               *PtrBuffer;
2438   UINT32              BlockSize;
2439   UINT32              ByteCount;
2440   UINT32              MaxBlock;
2441   UINT32              SectorCount;
2442   UINT32              NextSectorCount;
2443   UINT64              Timeout;
2444   EFI_STATUS          Status;
2445   UINT8               Index;
2446   UINT8               MaxRetry;
2447   BOOLEAN             NeedRetry;
2448 
2449   Status            = EFI_SUCCESS;
2450 
2451   BlocksRemaining   = NumberOfBlocks;
2452   BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;
2453 
2454   //
2455   // limit the data bytes that can be transferred by one Read(10) or Read(16) Command
2456   //
2457   if (!ScsiDiskDevice->Cdb16Byte) {
2458     MaxBlock         = 0xFFFF;
2459   } else {
2460     MaxBlock         = 0xFFFFFFFF;
2461   }
2462 
2463   PtrBuffer = Buffer;
2464 
2465   while (BlocksRemaining > 0) {
2466 
2467     if (BlocksRemaining <= MaxBlock) {
2468       if (!ScsiDiskDevice->Cdb16Byte) {
2469         SectorCount = (UINT16) BlocksRemaining;
2470       } else {
2471         SectorCount = (UINT32) BlocksRemaining;
2472       }
2473     } else {
2474       SectorCount = MaxBlock;
2475     }
2476 
2477     ByteCount = SectorCount * BlockSize;
2478     //
2479     // |------------------------|-----------------|------------------|-----------------|
2480     // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |
2481     // |------------------------|-----------------|------------------|-----------------|
2482     // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |
2483     // |------------------------|-----------------|------------------|-----------------|
2484     // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |
2485     // |------------------------|-----------------|------------------|-----------------|
2486     // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |
2487     // |------------------------|-----------------|------------------|-----------------|
2488     // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |
2489     // |------------------------|-----------------|------------------|-----------------|
2490     // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |
2491     // |------------------------|-----------------|------------------|-----------------|
2492     // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |
2493     // |------------------------|-----------------|------------------|-----------------|
2494     // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |
2495     // |------------------------|-----------------|------------------|-----------------|
2496     // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |
2497     // |------------------------|-----------------|------------------|-----------------|
2498     // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |
2499     // |------------------------|-----------------|------------------|-----------------|
2500     // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |
2501     // |------------------------|-----------------|------------------|-----------------|
2502     //
2503     // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use
2504     // the lowest transfer rate to calculate the possible maximum timeout value for each operation.
2505     // From the above table, we could know 2.1Mbytes per second is lowest one.
2506     // The timout value is rounded up to nearest integar and here an additional 30s is added
2507     // to follow ATA spec in which it mentioned that the device may take up to 30s to respond
2508     // commands in the Standby/Idle mode.
2509     //
2510     Timeout   = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);
2511     MaxRetry  = 2;
2512     for (Index = 0; Index < MaxRetry; Index++) {
2513       if (!ScsiDiskDevice->Cdb16Byte) {
2514         Status = ScsiDiskWrite10 (
2515                   ScsiDiskDevice,
2516                   &NeedRetry,
2517                   Timeout,
2518                   PtrBuffer,
2519                   &ByteCount,
2520                   (UINT32) Lba,
2521                   SectorCount
2522                   );
2523       } else {
2524         Status = ScsiDiskWrite16 (
2525                   ScsiDiskDevice,
2526                   &NeedRetry,
2527                   Timeout,
2528                   PtrBuffer,
2529                   &ByteCount,
2530                   Lba,
2531                   SectorCount
2532                   );
2533         }
2534       if (!EFI_ERROR (Status)) {
2535         break;
2536       }
2537 
2538       if (!NeedRetry) {
2539         return EFI_DEVICE_ERROR;
2540       }
2541 
2542       //
2543       // We need to retry. However, if ScsiDiskWrite10() or ScsiDiskWrite16()
2544       // has lowered ByteCount on output, we must make sure that we lower
2545       // SectorCount accordingly. SectorCount will be encoded in the CDB, and
2546       // it is invalid to request more sectors in the CDB than the entire
2547       // transfer (ie. ByteCount) can carry.
2548       //
2549       // In addition, ByteCount is only expected to go down, or stay unchaged.
2550       // Therefore we don't need to update Timeout: the original timeout should
2551       // accommodate shorter transfers too.
2552       //
2553       NextSectorCount = ByteCount / BlockSize;
2554       if (NextSectorCount < SectorCount) {
2555         SectorCount = NextSectorCount;
2556         //
2557         // Account for any rounding down.
2558         //
2559         ByteCount = SectorCount * BlockSize;
2560       }
2561     }
2562 
2563     if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {
2564       return EFI_DEVICE_ERROR;
2565     }
2566     //
2567     // actual transferred sectors
2568     //
2569     SectorCount = ByteCount / BlockSize;
2570 
2571     Lba += SectorCount;
2572     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
2573     BlocksRemaining -= SectorCount;
2574   }
2575 
2576   return EFI_SUCCESS;
2577 }
2578 
2579 /**
2580   Asynchronously read sector from SCSI Disk.
2581 
2582   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV.
2583   @param  Buffer          The buffer to fill in the read out data.
2584   @param  Lba             Logic block address.
2585   @param  NumberOfBlocks  The number of blocks to read.
2586   @param  Token           A pointer to the token associated with the
2587                           non-blocking read request.
2588 
2589   @retval EFI_INVALID_PARAMETER  Token is NULL or Token->Event is NULL.
2590   @retval EFI_DEVICE_ERROR       Indicates a device error.
2591   @retval EFI_SUCCESS            Operation is successful.
2592 
2593 **/
2594 EFI_STATUS
ScsiDiskAsyncReadSectors(IN SCSI_DISK_DEV * ScsiDiskDevice,OUT VOID * Buffer,IN EFI_LBA Lba,IN UINTN NumberOfBlocks,IN EFI_BLOCK_IO2_TOKEN * Token)2595 ScsiDiskAsyncReadSectors (
2596   IN   SCSI_DISK_DEV         *ScsiDiskDevice,
2597   OUT  VOID                  *Buffer,
2598   IN   EFI_LBA               Lba,
2599   IN   UINTN                 NumberOfBlocks,
2600   IN   EFI_BLOCK_IO2_TOKEN   *Token
2601   )
2602 {
2603   UINTN                 BlocksRemaining;
2604   UINT8                 *PtrBuffer;
2605   UINT32                BlockSize;
2606   UINT32                ByteCount;
2607   UINT32                MaxBlock;
2608   UINT32                SectorCount;
2609   UINT64                Timeout;
2610   SCSI_BLKIO2_REQUEST   *BlkIo2Req;
2611   EFI_STATUS            Status;
2612   EFI_TPL               OldTpl;
2613 
2614   if ((Token == NULL) || (Token->Event == NULL)) {
2615     return EFI_INVALID_PARAMETER;
2616   }
2617 
2618   BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));
2619   if (BlkIo2Req == NULL) {
2620     return EFI_OUT_OF_RESOURCES;
2621   }
2622 
2623   BlkIo2Req->Token  = Token;
2624 
2625   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
2626   InsertTailList (&ScsiDiskDevice->BlkIo2Queue, &BlkIo2Req->Link);
2627   gBS->RestoreTPL (OldTpl);
2628 
2629   InitializeListHead (&BlkIo2Req->ScsiRWQueue);
2630 
2631   Status            = EFI_SUCCESS;
2632 
2633   BlocksRemaining   = NumberOfBlocks;
2634   BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;
2635 
2636   //
2637   // Limit the data bytes that can be transferred by one Read(10) or Read(16)
2638   // Command
2639   //
2640   if (!ScsiDiskDevice->Cdb16Byte) {
2641     MaxBlock         = 0xFFFF;
2642   } else {
2643     MaxBlock         = 0xFFFFFFFF;
2644   }
2645 
2646   PtrBuffer = Buffer;
2647 
2648   while (BlocksRemaining > 0) {
2649 
2650     if (BlocksRemaining <= MaxBlock) {
2651       if (!ScsiDiskDevice->Cdb16Byte) {
2652         SectorCount = (UINT16) BlocksRemaining;
2653       } else {
2654         SectorCount = (UINT32) BlocksRemaining;
2655       }
2656     } else {
2657       SectorCount = MaxBlock;
2658     }
2659 
2660     ByteCount = SectorCount * BlockSize;
2661     //
2662     // |------------------------|-----------------|------------------|-----------------|
2663     // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |
2664     // |------------------------|-----------------|------------------|-----------------|
2665     // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |
2666     // |------------------------|-----------------|------------------|-----------------|
2667     // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |
2668     // |------------------------|-----------------|------------------|-----------------|
2669     // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |
2670     // |------------------------|-----------------|------------------|-----------------|
2671     // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |
2672     // |------------------------|-----------------|------------------|-----------------|
2673     // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |
2674     // |------------------------|-----------------|------------------|-----------------|
2675     // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |
2676     // |------------------------|-----------------|------------------|-----------------|
2677     // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |
2678     // |------------------------|-----------------|------------------|-----------------|
2679     // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |
2680     // |------------------------|-----------------|------------------|-----------------|
2681     // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |
2682     // |------------------------|-----------------|------------------|-----------------|
2683     // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |
2684     // |------------------------|-----------------|------------------|-----------------|
2685     //
2686     // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,
2687     // we have to use the lowest transfer rate to calculate the possible
2688     // maximum timeout value for each operation.
2689     // From the above table, we could know 2.1Mbytes per second is lowest one.
2690     // The timout value is rounded up to nearest integar and here an additional
2691     // 30s is added to follow ATA spec in which it mentioned that the device
2692     // may take up to 30s to respond commands in the Standby/Idle mode.
2693     //
2694     Timeout   = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);
2695 
2696     if (!ScsiDiskDevice->Cdb16Byte) {
2697       Status = ScsiDiskAsyncRead10 (
2698                  ScsiDiskDevice,
2699                  Timeout,
2700                  0,
2701                  PtrBuffer,
2702                  ByteCount,
2703                  (UINT32) Lba,
2704                  SectorCount,
2705                  BlkIo2Req,
2706                  Token
2707                  );
2708     } else {
2709       Status = ScsiDiskAsyncRead16 (
2710                  ScsiDiskDevice,
2711                  Timeout,
2712                  0,
2713                  PtrBuffer,
2714                  ByteCount,
2715                  Lba,
2716                  SectorCount,
2717                  BlkIo2Req,
2718                  Token
2719                  );
2720     }
2721     if (EFI_ERROR (Status)) {
2722       //
2723       // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data
2724       // length of a SCSI I/O command is too large.
2725       // In this case, we retry sending the SCSI command with a data length
2726       // half of its previous value.
2727       //
2728       if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {
2729         if ((MaxBlock > 1) && (SectorCount > 1)) {
2730           MaxBlock = MIN (MaxBlock, SectorCount) >> 1;
2731           continue;
2732         }
2733       }
2734 
2735       OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
2736       if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {
2737         //
2738         // Free the SCSI_BLKIO2_REQUEST structure only when there is no other
2739         // SCSI sub-task running. Otherwise, it will be freed in the callback
2740         // function ScsiDiskNotify().
2741         //
2742         RemoveEntryList (&BlkIo2Req->Link);
2743         FreePool (BlkIo2Req);
2744         BlkIo2Req = NULL;
2745         gBS->RestoreTPL (OldTpl);
2746 
2747         //
2748         // It is safe to return error status to the caller, since there is no
2749         // previous SCSI sub-task executing.
2750         //
2751         Status = EFI_DEVICE_ERROR;
2752         goto Done;
2753       } else {
2754         gBS->RestoreTPL (OldTpl);
2755 
2756         //
2757         // There are previous SCSI commands still running, EFI_SUCCESS should
2758         // be returned to make sure that the caller does not free resources
2759         // still using by these SCSI commands.
2760         //
2761         Status = EFI_SUCCESS;
2762         goto Done;
2763       }
2764     }
2765 
2766     //
2767     // Sectors submitted for transfer
2768     //
2769     SectorCount = ByteCount / BlockSize;
2770 
2771     Lba += SectorCount;
2772     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
2773     BlocksRemaining -= SectorCount;
2774   }
2775 
2776   Status = EFI_SUCCESS;
2777 
2778 Done:
2779   if (BlkIo2Req != NULL) {
2780     BlkIo2Req->LastScsiRW = TRUE;
2781 
2782     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
2783     if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {
2784       RemoveEntryList (&BlkIo2Req->Link);
2785       FreePool (BlkIo2Req);
2786       BlkIo2Req = NULL;
2787 
2788       gBS->SignalEvent (Token->Event);
2789     }
2790     gBS->RestoreTPL (OldTpl);
2791   }
2792 
2793   return Status;
2794 }
2795 
2796 /**
2797   Asynchronously write sector to SCSI Disk.
2798 
2799   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV.
2800   @param  Buffer          The buffer of data to be written into SCSI Disk.
2801   @param  Lba             Logic block address.
2802   @param  NumberOfBlocks  The number of blocks to read.
2803   @param  Token           A pointer to the token associated with the
2804                           non-blocking read request.
2805 
2806   @retval EFI_INVALID_PARAMETER  Token is NULL or Token->Event is NULL
2807   @retval EFI_DEVICE_ERROR  Indicates a device error.
2808   @retval EFI_SUCCESS       Operation is successful.
2809 
2810 **/
2811 EFI_STATUS
ScsiDiskAsyncWriteSectors(IN SCSI_DISK_DEV * ScsiDiskDevice,IN VOID * Buffer,IN EFI_LBA Lba,IN UINTN NumberOfBlocks,IN EFI_BLOCK_IO2_TOKEN * Token)2812 ScsiDiskAsyncWriteSectors (
2813   IN  SCSI_DISK_DEV          *ScsiDiskDevice,
2814   IN  VOID                   *Buffer,
2815   IN  EFI_LBA                Lba,
2816   IN  UINTN                  NumberOfBlocks,
2817   IN  EFI_BLOCK_IO2_TOKEN    *Token
2818   )
2819 {
2820   UINTN                 BlocksRemaining;
2821   UINT8                 *PtrBuffer;
2822   UINT32                BlockSize;
2823   UINT32                ByteCount;
2824   UINT32                MaxBlock;
2825   UINT32                SectorCount;
2826   UINT64                Timeout;
2827   SCSI_BLKIO2_REQUEST   *BlkIo2Req;
2828   EFI_STATUS            Status;
2829   EFI_TPL               OldTpl;
2830 
2831   if ((Token == NULL) || (Token->Event == NULL)) {
2832     return EFI_INVALID_PARAMETER;
2833   }
2834 
2835   BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));
2836   if (BlkIo2Req == NULL) {
2837     return EFI_OUT_OF_RESOURCES;
2838   }
2839 
2840   BlkIo2Req->Token  = Token;
2841 
2842   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
2843   InsertTailList (&ScsiDiskDevice->BlkIo2Queue, &BlkIo2Req->Link);
2844   gBS->RestoreTPL (OldTpl);
2845 
2846   InitializeListHead (&BlkIo2Req->ScsiRWQueue);
2847 
2848   Status            = EFI_SUCCESS;
2849 
2850   BlocksRemaining   = NumberOfBlocks;
2851   BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;
2852 
2853   //
2854   // Limit the data bytes that can be transferred by one Read(10) or Read(16)
2855   // Command
2856   //
2857   if (!ScsiDiskDevice->Cdb16Byte) {
2858     MaxBlock         = 0xFFFF;
2859   } else {
2860     MaxBlock         = 0xFFFFFFFF;
2861   }
2862 
2863   PtrBuffer = Buffer;
2864 
2865   while (BlocksRemaining > 0) {
2866 
2867     if (BlocksRemaining <= MaxBlock) {
2868       if (!ScsiDiskDevice->Cdb16Byte) {
2869         SectorCount = (UINT16) BlocksRemaining;
2870       } else {
2871         SectorCount = (UINT32) BlocksRemaining;
2872       }
2873     } else {
2874       SectorCount = MaxBlock;
2875     }
2876 
2877     ByteCount = SectorCount * BlockSize;
2878     //
2879     // |------------------------|-----------------|------------------|-----------------|
2880     // |   ATA Transfer Mode    |  Transfer Rate  |  SCSI Interface  |  Transfer Rate  |
2881     // |------------------------|-----------------|------------------|-----------------|
2882     // |       PIO Mode 0       |  3.3Mbytes/sec  |     SCSI-1       |    5Mbytes/sec  |
2883     // |------------------------|-----------------|------------------|-----------------|
2884     // |       PIO Mode 1       |  5.2Mbytes/sec  |    Fast SCSI     |   10Mbytes/sec  |
2885     // |------------------------|-----------------|------------------|-----------------|
2886     // |       PIO Mode 2       |  8.3Mbytes/sec  |  Fast-Wide SCSI  |   20Mbytes/sec  |
2887     // |------------------------|-----------------|------------------|-----------------|
2888     // |       PIO Mode 3       | 11.1Mbytes/sec  |    Ultra SCSI    |   20Mbytes/sec  |
2889     // |------------------------|-----------------|------------------|-----------------|
2890     // |       PIO Mode 4       | 16.6Mbytes/sec  |  Ultra Wide SCSI |   40Mbytes/sec  |
2891     // |------------------------|-----------------|------------------|-----------------|
2892     // | Single-word DMA Mode 0 |  2.1Mbytes/sec  |    Ultra2 SCSI   |   40Mbytes/sec  |
2893     // |------------------------|-----------------|------------------|-----------------|
2894     // | Single-word DMA Mode 1 |  4.2Mbytes/sec  | Ultra2 Wide SCSI |   80Mbytes/sec  |
2895     // |------------------------|-----------------|------------------|-----------------|
2896     // | Single-word DMA Mode 2 |  8.4Mbytes/sec  |    Ultra3 SCSI   |  160Mbytes/sec  |
2897     // |------------------------|-----------------|------------------|-----------------|
2898     // | Multi-word DMA Mode 0  |  4.2Mbytes/sec  |  Ultra-320 SCSI  |  320Mbytes/sec  |
2899     // |------------------------|-----------------|------------------|-----------------|
2900     // | Multi-word DMA Mode 1  | 13.3Mbytes/sec  |  Ultra-640 SCSI  |  640Mbytes/sec  |
2901     // |------------------------|-----------------|------------------|-----------------|
2902     //
2903     // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,
2904     // we have to use the lowest transfer rate to calculate the possible
2905     // maximum timeout value for each operation.
2906     // From the above table, we could know 2.1Mbytes per second is lowest one.
2907     // The timout value is rounded up to nearest integar and here an additional
2908     // 30s is added to follow ATA spec in which it mentioned that the device
2909     // may take up to 30s to respond commands in the Standby/Idle mode.
2910     //
2911     Timeout   = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);
2912 
2913     if (!ScsiDiskDevice->Cdb16Byte) {
2914       Status = ScsiDiskAsyncWrite10 (
2915                  ScsiDiskDevice,
2916                  Timeout,
2917                  0,
2918                  PtrBuffer,
2919                  ByteCount,
2920                  (UINT32) Lba,
2921                  SectorCount,
2922                  BlkIo2Req,
2923                  Token
2924                  );
2925     } else {
2926       Status = ScsiDiskAsyncWrite16 (
2927                  ScsiDiskDevice,
2928                  Timeout,
2929                  0,
2930                  PtrBuffer,
2931                  ByteCount,
2932                  Lba,
2933                  SectorCount,
2934                  BlkIo2Req,
2935                  Token
2936                  );
2937     }
2938     if (EFI_ERROR (Status)) {
2939       //
2940       // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data
2941       // length of a SCSI I/O command is too large.
2942       // In this case, we retry sending the SCSI command with a data length
2943       // half of its previous value.
2944       //
2945       if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {
2946         if ((MaxBlock > 1) && (SectorCount > 1)) {
2947           MaxBlock = MIN (MaxBlock, SectorCount) >> 1;
2948           continue;
2949         }
2950       }
2951 
2952       OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
2953       if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {
2954         //
2955         // Free the SCSI_BLKIO2_REQUEST structure only when there is no other
2956         // SCSI sub-task running. Otherwise, it will be freed in the callback
2957         // function ScsiDiskNotify().
2958         //
2959         RemoveEntryList (&BlkIo2Req->Link);
2960         FreePool (BlkIo2Req);
2961         BlkIo2Req = NULL;
2962         gBS->RestoreTPL (OldTpl);
2963 
2964         //
2965         // It is safe to return error status to the caller, since there is no
2966         // previous SCSI sub-task executing.
2967         //
2968         Status = EFI_DEVICE_ERROR;
2969         goto Done;
2970       } else {
2971         gBS->RestoreTPL (OldTpl);
2972 
2973         //
2974         // There are previous SCSI commands still running, EFI_SUCCESS should
2975         // be returned to make sure that the caller does not free resources
2976         // still using by these SCSI commands.
2977         //
2978         Status = EFI_SUCCESS;
2979         goto Done;
2980       }
2981     }
2982 
2983     //
2984     // Sectors submitted for transfer
2985     //
2986     SectorCount = ByteCount / BlockSize;
2987 
2988     Lba += SectorCount;
2989     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
2990     BlocksRemaining -= SectorCount;
2991   }
2992 
2993   Status = EFI_SUCCESS;
2994 
2995 Done:
2996   if (BlkIo2Req != NULL) {
2997     BlkIo2Req->LastScsiRW = TRUE;
2998 
2999     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
3000     if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {
3001       RemoveEntryList (&BlkIo2Req->Link);
3002       FreePool (BlkIo2Req);
3003       BlkIo2Req = NULL;
3004 
3005       gBS->SignalEvent (Token->Event);
3006     }
3007     gBS->RestoreTPL (OldTpl);
3008   }
3009 
3010   return Status;
3011 }
3012 
3013 
3014 /**
3015   Submit Read(10) command.
3016 
3017   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
3018   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
3019   @param  Timeout            The time to complete the command
3020   @param  DataBuffer         The buffer to fill with the read out data
3021   @param  DataLength         The length of buffer
3022   @param  StartLba           The start logic block address
3023   @param  SectorCount        The number of blocks to read
3024 
3025   @return  EFI_STATUS is returned by calling ScsiRead10Command().
3026 **/
3027 EFI_STATUS
ScsiDiskRead10(IN SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry,IN UINT64 Timeout,OUT UINT8 * DataBuffer,IN OUT UINT32 * DataLength,IN UINT32 StartLba,IN UINT32 SectorCount)3028 ScsiDiskRead10 (
3029   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
3030      OUT BOOLEAN               *NeedRetry,
3031   IN     UINT64                Timeout,
3032      OUT UINT8                 *DataBuffer,
3033   IN OUT UINT32                *DataLength,
3034   IN     UINT32                StartLba,
3035   IN     UINT32                SectorCount
3036   )
3037 {
3038   UINT8       SenseDataLength;
3039   EFI_STATUS  Status;
3040   EFI_STATUS  ReturnStatus;
3041   UINT8       HostAdapterStatus;
3042   UINT8       TargetStatus;
3043   UINTN       Action;
3044 
3045   //
3046   // Implement a backoff algorithem to resolve some compatibility issues that
3047   // some SCSI targets or ATAPI devices couldn't correctly response reading/writing
3048   // big data in a single operation.
3049   // This algorithem will at first try to execute original request. If the request fails
3050   // with media error sense data or else, it will reduce the transfer length to half and
3051   // try again till the operation succeeds or fails with one sector transfer length.
3052   //
3053 BackOff:
3054   *NeedRetry          = FALSE;
3055   Action              = ACTION_NO_ACTION;
3056   SenseDataLength     = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
3057   ReturnStatus = ScsiRead10Command (
3058                    ScsiDiskDevice->ScsiIo,
3059                    Timeout,
3060                    ScsiDiskDevice->SenseData,
3061                    &SenseDataLength,
3062                    &HostAdapterStatus,
3063                    &TargetStatus,
3064                    DataBuffer,
3065                    DataLength,
3066                    StartLba,
3067                    SectorCount
3068                    );
3069 
3070   if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {
3071     *NeedRetry = TRUE;
3072     return EFI_DEVICE_ERROR;
3073   } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {
3074     *NeedRetry = FALSE;
3075     return ReturnStatus;
3076   }
3077 
3078   //
3079   // go ahead to check HostAdapterStatus and TargetStatus
3080   // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
3081   //
3082   Status = CheckHostAdapterStatus (HostAdapterStatus);
3083   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
3084     *NeedRetry = TRUE;
3085     return EFI_DEVICE_ERROR;
3086   } else if (Status == EFI_DEVICE_ERROR) {
3087     //
3088     // reset the scsi channel
3089     //
3090     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
3091     *NeedRetry = FALSE;
3092     return EFI_DEVICE_ERROR;
3093   }
3094 
3095   Status = CheckTargetStatus (TargetStatus);
3096   if (Status == EFI_NOT_READY) {
3097     //
3098     // reset the scsi device
3099     //
3100     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
3101     *NeedRetry = TRUE;
3102     return EFI_DEVICE_ERROR;
3103   } else if (Status == EFI_DEVICE_ERROR) {
3104     *NeedRetry = FALSE;
3105     return EFI_DEVICE_ERROR;
3106   }
3107 
3108   if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
3109     DEBUG ((EFI_D_ERROR, "ScsiDiskRead10: Check Condition happened!\n"));
3110     Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
3111     if (Action == ACTION_RETRY_COMMAND_LATER) {
3112       *NeedRetry = TRUE;
3113       return EFI_DEVICE_ERROR;
3114     } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
3115       if (SectorCount <= 1) {
3116         //
3117         // Jump out if the operation still fails with one sector transfer length.
3118         //
3119         *NeedRetry = FALSE;
3120         return EFI_DEVICE_ERROR;
3121       }
3122       //
3123       // Try again with half length if the sense data shows we need to retry.
3124       //
3125       SectorCount >>= 1;
3126       *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
3127       goto BackOff;
3128     } else {
3129       *NeedRetry = FALSE;
3130       return EFI_DEVICE_ERROR;
3131     }
3132   }
3133 
3134   return ReturnStatus;
3135 }
3136 
3137 
3138 /**
3139   Submit Write(10) Command.
3140 
3141   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
3142   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
3143   @param  Timeout            The time to complete the command
3144   @param  DataBuffer         The buffer to fill with the read out data
3145   @param  DataLength         The length of buffer
3146   @param  StartLba           The start logic block address
3147   @param  SectorCount        The number of blocks to write
3148 
3149   @return  EFI_STATUS is returned by calling ScsiWrite10Command().
3150 
3151 **/
3152 EFI_STATUS
ScsiDiskWrite10(IN SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry,IN UINT64 Timeout,IN UINT8 * DataBuffer,IN OUT UINT32 * DataLength,IN UINT32 StartLba,IN UINT32 SectorCount)3153 ScsiDiskWrite10 (
3154   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
3155      OUT BOOLEAN               *NeedRetry,
3156   IN     UINT64                Timeout,
3157   IN     UINT8                 *DataBuffer,
3158   IN OUT UINT32                *DataLength,
3159   IN     UINT32                StartLba,
3160   IN     UINT32                SectorCount
3161   )
3162 {
3163   EFI_STATUS  Status;
3164   EFI_STATUS  ReturnStatus;
3165   UINT8       SenseDataLength;
3166   UINT8       HostAdapterStatus;
3167   UINT8       TargetStatus;
3168   UINTN       Action;
3169 
3170   //
3171   // Implement a backoff algorithem to resolve some compatibility issues that
3172   // some SCSI targets or ATAPI devices couldn't correctly response reading/writing
3173   // big data in a single operation.
3174   // This algorithem will at first try to execute original request. If the request fails
3175   // with media error sense data or else, it will reduce the transfer length to half and
3176   // try again till the operation succeeds or fails with one sector transfer length.
3177   //
3178 BackOff:
3179   *NeedRetry          = FALSE;
3180   Action              = ACTION_NO_ACTION;
3181   SenseDataLength     = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
3182   ReturnStatus = ScsiWrite10Command (
3183                    ScsiDiskDevice->ScsiIo,
3184                    Timeout,
3185                    ScsiDiskDevice->SenseData,
3186                    &SenseDataLength,
3187                    &HostAdapterStatus,
3188                    &TargetStatus,
3189                    DataBuffer,
3190                    DataLength,
3191                    StartLba,
3192                    SectorCount
3193                    );
3194   if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {
3195     *NeedRetry = TRUE;
3196     return EFI_DEVICE_ERROR;
3197   } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {
3198     *NeedRetry = FALSE;
3199     return ReturnStatus;
3200   }
3201 
3202   //
3203   // go ahead to check HostAdapterStatus and TargetStatus
3204   // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
3205   //
3206   Status = CheckHostAdapterStatus (HostAdapterStatus);
3207   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
3208     *NeedRetry = TRUE;
3209     return EFI_DEVICE_ERROR;
3210   } else if (Status == EFI_DEVICE_ERROR) {
3211     //
3212     // reset the scsi channel
3213     //
3214     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
3215     *NeedRetry = FALSE;
3216     return EFI_DEVICE_ERROR;
3217   }
3218 
3219   Status = CheckTargetStatus (TargetStatus);
3220   if (Status == EFI_NOT_READY) {
3221     //
3222     // reset the scsi device
3223     //
3224     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
3225     *NeedRetry = TRUE;
3226     return EFI_DEVICE_ERROR;
3227   } else if (Status == EFI_DEVICE_ERROR) {
3228     *NeedRetry = FALSE;
3229     return EFI_DEVICE_ERROR;
3230   }
3231 
3232   if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
3233     DEBUG ((EFI_D_ERROR, "ScsiDiskWrite10: Check Condition happened!\n"));
3234     Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
3235     if (Action == ACTION_RETRY_COMMAND_LATER) {
3236       *NeedRetry = TRUE;
3237       return EFI_DEVICE_ERROR;
3238     } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
3239       if (SectorCount <= 1) {
3240         //
3241         // Jump out if the operation still fails with one sector transfer length.
3242         //
3243         *NeedRetry = FALSE;
3244         return EFI_DEVICE_ERROR;
3245       }
3246       //
3247       // Try again with half length if the sense data shows we need to retry.
3248       //
3249       SectorCount >>= 1;
3250       *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
3251       goto BackOff;
3252     } else {
3253       *NeedRetry = FALSE;
3254       return EFI_DEVICE_ERROR;
3255     }
3256   }
3257 
3258   return ReturnStatus;
3259 }
3260 
3261 
3262 /**
3263   Submit Read(16) command.
3264 
3265   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
3266   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
3267   @param  Timeout            The time to complete the command
3268   @param  DataBuffer         The buffer to fill with the read out data
3269   @param  DataLength         The length of buffer
3270   @param  StartLba           The start logic block address
3271   @param  SectorCount        The number of blocks to read
3272 
3273   @return  EFI_STATUS is returned by calling ScsiRead16Command().
3274 **/
3275 EFI_STATUS
ScsiDiskRead16(IN SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry,IN UINT64 Timeout,OUT UINT8 * DataBuffer,IN OUT UINT32 * DataLength,IN UINT64 StartLba,IN UINT32 SectorCount)3276 ScsiDiskRead16 (
3277   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
3278      OUT BOOLEAN               *NeedRetry,
3279   IN     UINT64                Timeout,
3280      OUT UINT8                 *DataBuffer,
3281   IN OUT UINT32                *DataLength,
3282   IN     UINT64                StartLba,
3283   IN     UINT32                SectorCount
3284   )
3285 {
3286   UINT8       SenseDataLength;
3287   EFI_STATUS  Status;
3288   EFI_STATUS  ReturnStatus;
3289   UINT8       HostAdapterStatus;
3290   UINT8       TargetStatus;
3291   UINTN       Action;
3292 
3293   //
3294   // Implement a backoff algorithem to resolve some compatibility issues that
3295   // some SCSI targets or ATAPI devices couldn't correctly response reading/writing
3296   // big data in a single operation.
3297   // This algorithem will at first try to execute original request. If the request fails
3298   // with media error sense data or else, it will reduce the transfer length to half and
3299   // try again till the operation succeeds or fails with one sector transfer length.
3300   //
3301 BackOff:
3302   *NeedRetry          = FALSE;
3303   Action              = ACTION_NO_ACTION;
3304   SenseDataLength     = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
3305   ReturnStatus = ScsiRead16Command (
3306                    ScsiDiskDevice->ScsiIo,
3307                    Timeout,
3308                    ScsiDiskDevice->SenseData,
3309                    &SenseDataLength,
3310                    &HostAdapterStatus,
3311                    &TargetStatus,
3312                    DataBuffer,
3313                    DataLength,
3314                    StartLba,
3315                    SectorCount
3316                    );
3317   if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {
3318     *NeedRetry = TRUE;
3319     return EFI_DEVICE_ERROR;
3320   } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {
3321     *NeedRetry = FALSE;
3322     return ReturnStatus;
3323   }
3324 
3325   //
3326   // go ahead to check HostAdapterStatus and TargetStatus
3327   // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
3328   //
3329   Status = CheckHostAdapterStatus (HostAdapterStatus);
3330   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
3331     *NeedRetry = TRUE;
3332     return EFI_DEVICE_ERROR;
3333   } else if (Status == EFI_DEVICE_ERROR) {
3334     //
3335     // reset the scsi channel
3336     //
3337     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
3338     *NeedRetry = FALSE;
3339     return EFI_DEVICE_ERROR;
3340   }
3341 
3342   Status = CheckTargetStatus (TargetStatus);
3343   if (Status == EFI_NOT_READY) {
3344     //
3345     // reset the scsi device
3346     //
3347     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
3348     *NeedRetry = TRUE;
3349     return EFI_DEVICE_ERROR;
3350   } else if (Status == EFI_DEVICE_ERROR) {
3351     *NeedRetry = FALSE;
3352     return EFI_DEVICE_ERROR;
3353   }
3354 
3355   if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
3356     DEBUG ((EFI_D_ERROR, "ScsiDiskRead16: Check Condition happened!\n"));
3357     Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
3358     if (Action == ACTION_RETRY_COMMAND_LATER) {
3359       *NeedRetry = TRUE;
3360       return EFI_DEVICE_ERROR;
3361     } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
3362       if (SectorCount <= 1) {
3363         //
3364         // Jump out if the operation still fails with one sector transfer length.
3365         //
3366         *NeedRetry = FALSE;
3367         return EFI_DEVICE_ERROR;
3368       }
3369       //
3370       // Try again with half length if the sense data shows we need to retry.
3371       //
3372       SectorCount >>= 1;
3373       *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
3374       goto BackOff;
3375     } else {
3376       *NeedRetry = FALSE;
3377       return EFI_DEVICE_ERROR;
3378     }
3379   }
3380 
3381   return ReturnStatus;
3382 }
3383 
3384 
3385 /**
3386   Submit Write(16) Command.
3387 
3388   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
3389   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
3390   @param  Timeout            The time to complete the command
3391   @param  DataBuffer         The buffer to fill with the read out data
3392   @param  DataLength         The length of buffer
3393   @param  StartLba           The start logic block address
3394   @param  SectorCount        The number of blocks to write
3395 
3396   @return  EFI_STATUS is returned by calling ScsiWrite16Command().
3397 
3398 **/
3399 EFI_STATUS
ScsiDiskWrite16(IN SCSI_DISK_DEV * ScsiDiskDevice,OUT BOOLEAN * NeedRetry,IN UINT64 Timeout,IN UINT8 * DataBuffer,IN OUT UINT32 * DataLength,IN UINT64 StartLba,IN UINT32 SectorCount)3400 ScsiDiskWrite16 (
3401   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
3402      OUT BOOLEAN               *NeedRetry,
3403   IN     UINT64                Timeout,
3404   IN     UINT8                 *DataBuffer,
3405   IN OUT UINT32                *DataLength,
3406   IN     UINT64                StartLba,
3407   IN     UINT32                SectorCount
3408   )
3409 {
3410   EFI_STATUS  Status;
3411   EFI_STATUS  ReturnStatus;
3412   UINT8       SenseDataLength;
3413   UINT8       HostAdapterStatus;
3414   UINT8       TargetStatus;
3415   UINTN       Action;
3416 
3417   //
3418   // Implement a backoff algorithem to resolve some compatibility issues that
3419   // some SCSI targets or ATAPI devices couldn't correctly response reading/writing
3420   // big data in a single operation.
3421   // This algorithem will at first try to execute original request. If the request fails
3422   // with media error sense data or else, it will reduce the transfer length to half and
3423   // try again till the operation succeeds or fails with one sector transfer length.
3424   //
3425 BackOff:
3426   *NeedRetry          = FALSE;
3427   Action              = ACTION_NO_ACTION;
3428   SenseDataLength     = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));
3429   ReturnStatus = ScsiWrite16Command (
3430                    ScsiDiskDevice->ScsiIo,
3431                    Timeout,
3432                    ScsiDiskDevice->SenseData,
3433                    &SenseDataLength,
3434                    &HostAdapterStatus,
3435                    &TargetStatus,
3436                    DataBuffer,
3437                    DataLength,
3438                    StartLba,
3439                    SectorCount
3440                    );
3441   if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {
3442     *NeedRetry = TRUE;
3443     return EFI_DEVICE_ERROR;
3444   } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {
3445     *NeedRetry = FALSE;
3446     return ReturnStatus;
3447   }
3448 
3449   //
3450   // go ahead to check HostAdapterStatus and TargetStatus
3451   // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
3452   //
3453   Status = CheckHostAdapterStatus (HostAdapterStatus);
3454   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
3455     *NeedRetry = TRUE;
3456     return EFI_DEVICE_ERROR;
3457   } else if (Status == EFI_DEVICE_ERROR) {
3458     //
3459     // reset the scsi channel
3460     //
3461     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
3462     *NeedRetry = FALSE;
3463     return EFI_DEVICE_ERROR;
3464   }
3465 
3466   Status = CheckTargetStatus (TargetStatus);
3467   if (Status == EFI_NOT_READY) {
3468     //
3469     // reset the scsi device
3470     //
3471     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
3472     *NeedRetry = TRUE;
3473     return EFI_DEVICE_ERROR;
3474   } else if (Status == EFI_DEVICE_ERROR) {
3475     *NeedRetry = FALSE;
3476     return EFI_DEVICE_ERROR;
3477   }
3478 
3479   if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
3480     DEBUG ((EFI_D_ERROR, "ScsiDiskWrite16: Check Condition happened!\n"));
3481     Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);
3482     if (Action == ACTION_RETRY_COMMAND_LATER) {
3483       *NeedRetry = TRUE;
3484       return EFI_DEVICE_ERROR;
3485     } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
3486       if (SectorCount <= 1) {
3487         //
3488         // Jump out if the operation still fails with one sector transfer length.
3489         //
3490         *NeedRetry = FALSE;
3491         return EFI_DEVICE_ERROR;
3492       }
3493       //
3494       // Try again with half length if the sense data shows we need to retry.
3495       //
3496       SectorCount >>= 1;
3497       *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
3498       goto BackOff;
3499     } else {
3500       *NeedRetry = FALSE;
3501       return EFI_DEVICE_ERROR;
3502     }
3503   }
3504 
3505   return ReturnStatus;
3506 }
3507 
3508 
3509 /**
3510   Internal helper notify function in which determine whether retry of a SCSI
3511   Read/Write command is needed and signal the event passed from Block I/O(2) if
3512   the SCSI I/O operation completes.
3513 
3514   @param  Event    The instance of EFI_EVENT.
3515   @param  Context  The parameter passed in.
3516 
3517 **/
3518 VOID
3519 EFIAPI
ScsiDiskNotify(IN EFI_EVENT Event,IN VOID * Context)3520 ScsiDiskNotify (
3521   IN  EFI_EVENT  Event,
3522   IN  VOID       *Context
3523   )
3524 {
3525   EFI_STATUS                       Status;
3526   SCSI_ASYNC_RW_REQUEST            *Request;
3527   SCSI_DISK_DEV                    *ScsiDiskDevice;
3528   EFI_BLOCK_IO2_TOKEN              *Token;
3529   UINTN                            Action;
3530   UINT32                           OldDataLength;
3531   UINT32                           OldSectorCount;
3532   UINT8                            MaxRetry;
3533 
3534   gBS->CloseEvent (Event);
3535 
3536   Request         = (SCSI_ASYNC_RW_REQUEST *) Context;
3537   ScsiDiskDevice  = Request->ScsiDiskDevice;
3538   Token           = Request->BlkIo2Req->Token;
3539   OldDataLength   = Request->DataLength;
3540   OldSectorCount  = Request->SectorCount;
3541   MaxRetry        = 2;
3542 
3543   //
3544   // If previous sub-tasks already fails, no need to process this sub-task.
3545   //
3546   if (Token->TransactionStatus != EFI_SUCCESS) {
3547     goto Exit;
3548   }
3549 
3550   //
3551   // Check HostAdapterStatus and TargetStatus
3552   // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
3553   //
3554   Status = CheckHostAdapterStatus (Request->HostAdapterStatus);
3555   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
3556     if (++Request->TimesRetry > MaxRetry) {
3557       Token->TransactionStatus = EFI_DEVICE_ERROR;
3558       goto Exit;
3559     } else {
3560       goto Retry;
3561     }
3562   } else if (Status == EFI_DEVICE_ERROR) {
3563     //
3564     // reset the scsi channel
3565     //
3566     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
3567     Token->TransactionStatus = EFI_DEVICE_ERROR;
3568     goto Exit;
3569   }
3570 
3571   Status = CheckTargetStatus (Request->TargetStatus);
3572   if (Status == EFI_NOT_READY) {
3573     //
3574     // reset the scsi device
3575     //
3576     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
3577     if (++Request->TimesRetry > MaxRetry) {
3578       Token->TransactionStatus = EFI_DEVICE_ERROR;
3579       goto Exit;
3580     } else {
3581       goto Retry;
3582     }
3583   } else if (Status == EFI_DEVICE_ERROR) {
3584     Token->TransactionStatus = EFI_DEVICE_ERROR;
3585     goto Exit;
3586   }
3587 
3588   if (Request->TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {
3589     DEBUG ((EFI_D_ERROR, "ScsiDiskNotify: Check Condition happened!\n"));
3590 
3591     Status = DetectMediaParsingSenseKeys (
3592                ScsiDiskDevice,
3593                Request->SenseData,
3594                Request->SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA),
3595                &Action
3596                );
3597     if (Action == ACTION_RETRY_COMMAND_LATER) {
3598       if (++Request->TimesRetry > MaxRetry) {
3599         Token->TransactionStatus = EFI_DEVICE_ERROR;
3600         goto Exit;
3601       } else {
3602         goto Retry;
3603       }
3604     } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {
3605       if (Request->SectorCount <= 1) {
3606         //
3607         // Jump out if the operation still fails with one sector transfer
3608         // length.
3609         //
3610         Token->TransactionStatus = EFI_DEVICE_ERROR;
3611         goto Exit;
3612       }
3613       //
3614       // Try again with two half length request if the sense data shows we need
3615       // to retry.
3616       //
3617       Request->SectorCount >>= 1;
3618       Request->DataLength = Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
3619       Request->TimesRetry  = 0;
3620 
3621       goto Retry;
3622     } else {
3623       Token->TransactionStatus = EFI_DEVICE_ERROR;
3624       goto Exit;
3625     }
3626   }
3627 
3628   //
3629   // This sub-task succeeds, no need to retry.
3630   //
3631   goto Exit;
3632 
3633 Retry:
3634   if (Request->InBuffer != NULL) {
3635     //
3636     // SCSI read command
3637     //
3638     if (!ScsiDiskDevice->Cdb16Byte) {
3639       Status = ScsiDiskAsyncRead10 (
3640                  ScsiDiskDevice,
3641                  Request->Timeout,
3642                  Request->TimesRetry,
3643                  Request->InBuffer,
3644                  Request->DataLength,
3645                  (UINT32) Request->StartLba,
3646                  Request->SectorCount,
3647                  Request->BlkIo2Req,
3648                  Token
3649                  );
3650     } else {
3651       Status = ScsiDiskAsyncRead16 (
3652                  ScsiDiskDevice,
3653                  Request->Timeout,
3654                  Request->TimesRetry,
3655                  Request->InBuffer,
3656                  Request->DataLength,
3657                  Request->StartLba,
3658                  Request->SectorCount,
3659                  Request->BlkIo2Req,
3660                  Token
3661                  );
3662     }
3663 
3664     if (EFI_ERROR (Status)) {
3665       Token->TransactionStatus = EFI_DEVICE_ERROR;
3666       goto Exit;
3667     } else if (OldSectorCount != Request->SectorCount) {
3668       //
3669       // Original sub-task will be split into two new sub-tasks with smaller
3670       // DataLength
3671       //
3672       if (!ScsiDiskDevice->Cdb16Byte) {
3673         Status = ScsiDiskAsyncRead10 (
3674                    ScsiDiskDevice,
3675                    Request->Timeout,
3676                    0,
3677                    Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,
3678                    OldDataLength - Request->DataLength,
3679                    (UINT32) Request->StartLba + Request->SectorCount,
3680                    OldSectorCount - Request->SectorCount,
3681                    Request->BlkIo2Req,
3682                    Token
3683                    );
3684       } else {
3685         Status = ScsiDiskAsyncRead16 (
3686                    ScsiDiskDevice,
3687                    Request->Timeout,
3688                    0,
3689                    Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,
3690                    OldDataLength - Request->DataLength,
3691                    Request->StartLba + Request->SectorCount,
3692                    OldSectorCount - Request->SectorCount,
3693                    Request->BlkIo2Req,
3694                    Token
3695                    );
3696       }
3697       if (EFI_ERROR (Status)) {
3698         Token->TransactionStatus = EFI_DEVICE_ERROR;
3699         goto Exit;
3700       }
3701     }
3702   } else {
3703     //
3704     // SCSI write command
3705     //
3706     if (!ScsiDiskDevice->Cdb16Byte) {
3707       Status = ScsiDiskAsyncWrite10 (
3708                  ScsiDiskDevice,
3709                  Request->Timeout,
3710                  Request->TimesRetry,
3711                  Request->OutBuffer,
3712                  Request->DataLength,
3713                  (UINT32) Request->StartLba,
3714                  Request->SectorCount,
3715                  Request->BlkIo2Req,
3716                  Token
3717                  );
3718     } else {
3719       Status = ScsiDiskAsyncWrite16 (
3720                  ScsiDiskDevice,
3721                  Request->Timeout,
3722                  Request->TimesRetry,
3723                  Request->OutBuffer,
3724                  Request->DataLength,
3725                  Request->StartLba,
3726                  Request->SectorCount,
3727                  Request->BlkIo2Req,
3728                  Token
3729                  );
3730     }
3731 
3732     if (EFI_ERROR (Status)) {
3733       Token->TransactionStatus = EFI_DEVICE_ERROR;
3734       goto Exit;
3735     } else if (OldSectorCount != Request->SectorCount) {
3736       //
3737       // Original sub-task will be split into two new sub-tasks with smaller
3738       // DataLength
3739       //
3740       if (!ScsiDiskDevice->Cdb16Byte) {
3741         Status = ScsiDiskAsyncWrite10 (
3742                    ScsiDiskDevice,
3743                    Request->Timeout,
3744                    0,
3745                    Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,
3746                    OldDataLength - Request->DataLength,
3747                    (UINT32) Request->StartLba + Request->SectorCount,
3748                    OldSectorCount - Request->SectorCount,
3749                    Request->BlkIo2Req,
3750                    Token
3751                    );
3752       } else {
3753         Status = ScsiDiskAsyncWrite16 (
3754                    ScsiDiskDevice,
3755                    Request->Timeout,
3756                    0,
3757                    Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,
3758                    OldDataLength - Request->DataLength,
3759                    Request->StartLba + Request->SectorCount,
3760                    OldSectorCount - Request->SectorCount,
3761                    Request->BlkIo2Req,
3762                    Token
3763                    );
3764       }
3765       if (EFI_ERROR (Status)) {
3766         Token->TransactionStatus = EFI_DEVICE_ERROR;
3767         goto Exit;
3768       }
3769     }
3770   }
3771 
3772 Exit:
3773   RemoveEntryList (&Request->Link);
3774   if ((IsListEmpty (&Request->BlkIo2Req->ScsiRWQueue)) &&
3775       (Request->BlkIo2Req->LastScsiRW)) {
3776     //
3777     // The last SCSI R/W command of a BlockIo2 request completes
3778     //
3779     RemoveEntryList (&Request->BlkIo2Req->Link);
3780     FreePool (Request->BlkIo2Req);  // Should be freed only once
3781     gBS->SignalEvent (Token->Event);
3782   }
3783 
3784   FreePool (Request->SenseData);
3785   FreePool (Request);
3786 }
3787 
3788 
3789 /**
3790   Submit Async Read(10) command.
3791 
3792   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.
3793   @param  Timeout            The time to complete the command.
3794   @param  TimesRetry         The number of times the command has been retried.
3795   @param  DataBuffer         The buffer to fill with the read out data.
3796   @param  DataLength         The length of buffer.
3797   @param  StartLba           The start logic block address.
3798   @param  SectorCount        The number of blocks to read.
3799   @param  BlkIo2Req          The upstream BlockIo2 request.
3800   @param  Token              The pointer to the token associated with the
3801                              non-blocking read request.
3802 
3803   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
3804                                 lack of resources.
3805   @return others                Status returned by calling
3806                                 ScsiRead10CommandEx().
3807 
3808 **/
3809 EFI_STATUS
ScsiDiskAsyncRead10(IN SCSI_DISK_DEV * ScsiDiskDevice,IN UINT64 Timeout,IN UINT8 TimesRetry,OUT UINT8 * DataBuffer,IN UINT32 DataLength,IN UINT32 StartLba,IN UINT32 SectorCount,IN OUT SCSI_BLKIO2_REQUEST * BlkIo2Req,IN EFI_BLOCK_IO2_TOKEN * Token)3810 ScsiDiskAsyncRead10 (
3811   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
3812   IN     UINT64                Timeout,
3813   IN     UINT8                 TimesRetry,
3814      OUT UINT8                 *DataBuffer,
3815   IN     UINT32                DataLength,
3816   IN     UINT32                StartLba,
3817   IN     UINT32                SectorCount,
3818   IN OUT SCSI_BLKIO2_REQUEST   *BlkIo2Req,
3819   IN     EFI_BLOCK_IO2_TOKEN   *Token
3820   )
3821 {
3822   EFI_STATUS                   Status;
3823   SCSI_ASYNC_RW_REQUEST        *Request;
3824   EFI_EVENT                    AsyncIoEvent;
3825   EFI_TPL                      OldTpl;
3826 
3827   AsyncIoEvent = NULL;
3828 
3829   Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));
3830   if (Request == NULL) {
3831     return EFI_OUT_OF_RESOURCES;
3832   }
3833 
3834   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
3835   InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);
3836   gBS->RestoreTPL (OldTpl);
3837 
3838   Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));
3839   Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);
3840   if (Request->SenseData == NULL) {
3841     Status = EFI_OUT_OF_RESOURCES;
3842     goto ErrorExit;
3843   }
3844 
3845   Request->ScsiDiskDevice  = ScsiDiskDevice;
3846   Request->Timeout         = Timeout;
3847   Request->TimesRetry      = TimesRetry;
3848   Request->InBuffer        = DataBuffer;
3849   Request->DataLength      = DataLength;
3850   Request->StartLba        = StartLba;
3851   Request->SectorCount     = SectorCount;
3852   Request->BlkIo2Req       = BlkIo2Req;
3853 
3854   //
3855   // Create Event
3856   //
3857   Status = gBS->CreateEvent (
3858                   EVT_NOTIFY_SIGNAL,
3859                   TPL_NOTIFY,
3860                   ScsiDiskNotify,
3861                   Request,
3862                   &AsyncIoEvent
3863                   );
3864   if (EFI_ERROR(Status)) {
3865     goto ErrorExit;
3866   }
3867 
3868   Status = ScsiRead10CommandEx (
3869              ScsiDiskDevice->ScsiIo,
3870              Request->Timeout,
3871              Request->SenseData,
3872              &Request->SenseDataLength,
3873              &Request->HostAdapterStatus,
3874              &Request->TargetStatus,
3875              Request->InBuffer,
3876              &Request->DataLength,
3877              (UINT32) Request->StartLba,
3878              Request->SectorCount,
3879              AsyncIoEvent
3880              );
3881   if (EFI_ERROR(Status)) {
3882     goto ErrorExit;
3883   }
3884 
3885   return EFI_SUCCESS;
3886 
3887 ErrorExit:
3888   if (AsyncIoEvent != NULL) {
3889     gBS->CloseEvent (AsyncIoEvent);
3890   }
3891 
3892   if (Request != NULL) {
3893     if (Request->SenseData != NULL) {
3894       FreePool (Request->SenseData);
3895     }
3896 
3897     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
3898     RemoveEntryList (&Request->Link);
3899     gBS->RestoreTPL (OldTpl);
3900 
3901     FreePool (Request);
3902   }
3903 
3904   return Status;
3905 }
3906 
3907 
3908 /**
3909   Submit Async Write(10) command.
3910 
3911   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.
3912   @param  Timeout            The time to complete the command.
3913   @param  TimesRetry         The number of times the command has been retried.
3914   @param  DataBuffer         The buffer contains the data to write.
3915   @param  DataLength         The length of buffer.
3916   @param  StartLba           The start logic block address.
3917   @param  SectorCount        The number of blocks to write.
3918   @param  BlkIo2Req          The upstream BlockIo2 request.
3919   @param  Token              The pointer to the token associated with the
3920                              non-blocking read request.
3921 
3922   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
3923                                 lack of resources.
3924   @return others                Status returned by calling
3925                                 ScsiWrite10CommandEx().
3926 
3927 **/
3928 EFI_STATUS
ScsiDiskAsyncWrite10(IN SCSI_DISK_DEV * ScsiDiskDevice,IN UINT64 Timeout,IN UINT8 TimesRetry,IN UINT8 * DataBuffer,IN UINT32 DataLength,IN UINT32 StartLba,IN UINT32 SectorCount,IN OUT SCSI_BLKIO2_REQUEST * BlkIo2Req,IN EFI_BLOCK_IO2_TOKEN * Token)3929 ScsiDiskAsyncWrite10 (
3930   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
3931   IN     UINT64                Timeout,
3932   IN     UINT8                 TimesRetry,
3933   IN     UINT8                 *DataBuffer,
3934   IN     UINT32                DataLength,
3935   IN     UINT32                StartLba,
3936   IN     UINT32                SectorCount,
3937   IN OUT SCSI_BLKIO2_REQUEST   *BlkIo2Req,
3938   IN     EFI_BLOCK_IO2_TOKEN   *Token
3939   )
3940 {
3941   EFI_STATUS                   Status;
3942   SCSI_ASYNC_RW_REQUEST        *Request;
3943   EFI_EVENT                    AsyncIoEvent;
3944   EFI_TPL                      OldTpl;
3945 
3946   AsyncIoEvent = NULL;
3947 
3948   Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));
3949   if (Request == NULL) {
3950     return EFI_OUT_OF_RESOURCES;
3951   }
3952 
3953   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
3954   InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);
3955   gBS->RestoreTPL (OldTpl);
3956 
3957   Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));
3958   Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);
3959   if (Request->SenseData == NULL) {
3960     Status = EFI_OUT_OF_RESOURCES;
3961     goto ErrorExit;
3962   }
3963 
3964   Request->ScsiDiskDevice  = ScsiDiskDevice;
3965   Request->Timeout         = Timeout;
3966   Request->TimesRetry      = TimesRetry;
3967   Request->OutBuffer       = DataBuffer;
3968   Request->DataLength      = DataLength;
3969   Request->StartLba        = StartLba;
3970   Request->SectorCount     = SectorCount;
3971   Request->BlkIo2Req       = BlkIo2Req;
3972 
3973   //
3974   // Create Event
3975   //
3976   Status = gBS->CreateEvent (
3977                   EVT_NOTIFY_SIGNAL,
3978                   TPL_NOTIFY,
3979                   ScsiDiskNotify,
3980                   Request,
3981                   &AsyncIoEvent
3982                   );
3983   if (EFI_ERROR(Status)) {
3984     goto ErrorExit;
3985   }
3986 
3987   Status = ScsiWrite10CommandEx (
3988              ScsiDiskDevice->ScsiIo,
3989              Request->Timeout,
3990              Request->SenseData,
3991              &Request->SenseDataLength,
3992              &Request->HostAdapterStatus,
3993              &Request->TargetStatus,
3994              Request->OutBuffer,
3995              &Request->DataLength,
3996              (UINT32) Request->StartLba,
3997              Request->SectorCount,
3998              AsyncIoEvent
3999              );
4000   if (EFI_ERROR(Status)) {
4001     goto ErrorExit;
4002   }
4003 
4004   return EFI_SUCCESS;
4005 
4006 ErrorExit:
4007   if (AsyncIoEvent != NULL) {
4008     gBS->CloseEvent (AsyncIoEvent);
4009   }
4010 
4011   if (Request != NULL) {
4012     if (Request->SenseData != NULL) {
4013       FreePool (Request->SenseData);
4014     }
4015 
4016     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
4017     RemoveEntryList (&Request->Link);
4018     gBS->RestoreTPL (OldTpl);
4019 
4020     FreePool (Request);
4021   }
4022 
4023   return Status;
4024 }
4025 
4026 
4027 /**
4028   Submit Async Read(16) command.
4029 
4030   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.
4031   @param  Timeout            The time to complete the command.
4032   @param  TimesRetry         The number of times the command has been retried.
4033   @param  DataBuffer         The buffer to fill with the read out data.
4034   @param  DataLength         The length of buffer.
4035   @param  StartLba           The start logic block address.
4036   @param  SectorCount        The number of blocks to read.
4037   @param  BlkIo2Req          The upstream BlockIo2 request.
4038   @param  Token              The pointer to the token associated with the
4039                              non-blocking read request.
4040 
4041   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
4042                                 lack of resources.
4043   @return others                Status returned by calling
4044                                 ScsiRead16CommandEx().
4045 
4046 **/
4047 EFI_STATUS
ScsiDiskAsyncRead16(IN SCSI_DISK_DEV * ScsiDiskDevice,IN UINT64 Timeout,IN UINT8 TimesRetry,OUT UINT8 * DataBuffer,IN UINT32 DataLength,IN UINT64 StartLba,IN UINT32 SectorCount,IN OUT SCSI_BLKIO2_REQUEST * BlkIo2Req,IN EFI_BLOCK_IO2_TOKEN * Token)4048 ScsiDiskAsyncRead16 (
4049   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
4050   IN     UINT64                Timeout,
4051   IN     UINT8                 TimesRetry,
4052      OUT UINT8                 *DataBuffer,
4053   IN     UINT32                DataLength,
4054   IN     UINT64                StartLba,
4055   IN     UINT32                SectorCount,
4056   IN OUT SCSI_BLKIO2_REQUEST   *BlkIo2Req,
4057   IN     EFI_BLOCK_IO2_TOKEN   *Token
4058   )
4059 {
4060   EFI_STATUS                   Status;
4061   SCSI_ASYNC_RW_REQUEST        *Request;
4062   EFI_EVENT                    AsyncIoEvent;
4063   EFI_TPL                      OldTpl;
4064 
4065   AsyncIoEvent = NULL;
4066 
4067   Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));
4068   if (Request == NULL) {
4069     return EFI_OUT_OF_RESOURCES;
4070   }
4071 
4072   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
4073   InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);
4074   gBS->RestoreTPL (OldTpl);
4075 
4076   Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));
4077   Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);
4078   if (Request->SenseData == NULL) {
4079     Status = EFI_OUT_OF_RESOURCES;
4080     goto ErrorExit;
4081   }
4082 
4083   Request->ScsiDiskDevice  = ScsiDiskDevice;
4084   Request->Timeout         = Timeout;
4085   Request->TimesRetry      = TimesRetry;
4086   Request->InBuffer        = DataBuffer;
4087   Request->DataLength      = DataLength;
4088   Request->StartLba        = StartLba;
4089   Request->SectorCount     = SectorCount;
4090   Request->BlkIo2Req       = BlkIo2Req;
4091 
4092   //
4093   // Create Event
4094   //
4095   Status = gBS->CreateEvent (
4096                   EVT_NOTIFY_SIGNAL,
4097                   TPL_NOTIFY,
4098                   ScsiDiskNotify,
4099                   Request,
4100                   &AsyncIoEvent
4101                   );
4102   if (EFI_ERROR(Status)) {
4103     goto ErrorExit;
4104   }
4105 
4106   Status = ScsiRead16CommandEx (
4107              ScsiDiskDevice->ScsiIo,
4108              Request->Timeout,
4109              Request->SenseData,
4110              &Request->SenseDataLength,
4111              &Request->HostAdapterStatus,
4112              &Request->TargetStatus,
4113              Request->InBuffer,
4114              &Request->DataLength,
4115              Request->StartLba,
4116              Request->SectorCount,
4117              AsyncIoEvent
4118              );
4119   if (EFI_ERROR(Status)) {
4120     goto ErrorExit;
4121   }
4122 
4123   return EFI_SUCCESS;
4124 
4125 ErrorExit:
4126   if (AsyncIoEvent != NULL) {
4127     gBS->CloseEvent (AsyncIoEvent);
4128   }
4129 
4130   if (Request != NULL) {
4131     if (Request->SenseData != NULL) {
4132       FreePool (Request->SenseData);
4133     }
4134 
4135     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
4136     RemoveEntryList (&Request->Link);
4137     gBS->RestoreTPL (OldTpl);
4138 
4139     FreePool (Request);
4140   }
4141 
4142   return Status;
4143 }
4144 
4145 
4146 /**
4147   Submit Async Write(16) command.
4148 
4149   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice.
4150   @param  Timeout            The time to complete the command.
4151   @param  TimesRetry         The number of times the command has been retried.
4152   @param  DataBuffer         The buffer contains the data to write.
4153   @param  DataLength         The length of buffer.
4154   @param  StartLba           The start logic block address.
4155   @param  SectorCount        The number of blocks to write.
4156   @param  BlkIo2Req          The upstream BlockIo2 request.
4157   @param  Token              The pointer to the token associated with the
4158                              non-blocking read request.
4159 
4160   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
4161                                 lack of resources.
4162   @return others                Status returned by calling
4163                                 ScsiWrite16CommandEx().
4164 
4165 **/
4166 EFI_STATUS
ScsiDiskAsyncWrite16(IN SCSI_DISK_DEV * ScsiDiskDevice,IN UINT64 Timeout,IN UINT8 TimesRetry,IN UINT8 * DataBuffer,IN UINT32 DataLength,IN UINT64 StartLba,IN UINT32 SectorCount,IN OUT SCSI_BLKIO2_REQUEST * BlkIo2Req,IN EFI_BLOCK_IO2_TOKEN * Token)4167 ScsiDiskAsyncWrite16 (
4168   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
4169   IN     UINT64                Timeout,
4170   IN     UINT8                 TimesRetry,
4171   IN     UINT8                 *DataBuffer,
4172   IN     UINT32                DataLength,
4173   IN     UINT64                StartLba,
4174   IN     UINT32                SectorCount,
4175   IN OUT SCSI_BLKIO2_REQUEST   *BlkIo2Req,
4176   IN     EFI_BLOCK_IO2_TOKEN   *Token
4177   )
4178 {
4179   EFI_STATUS                   Status;
4180   SCSI_ASYNC_RW_REQUEST        *Request;
4181   EFI_EVENT                    AsyncIoEvent;
4182   EFI_TPL                      OldTpl;
4183 
4184   AsyncIoEvent = NULL;
4185 
4186   Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));
4187   if (Request == NULL) {
4188     return EFI_OUT_OF_RESOURCES;
4189   }
4190 
4191   OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
4192   InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);
4193   gBS->RestoreTPL (OldTpl);
4194 
4195   Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));
4196   Request->SenseData       = AllocateZeroPool (Request->SenseDataLength);
4197   if (Request->SenseData == NULL) {
4198     Status = EFI_OUT_OF_RESOURCES;
4199     goto ErrorExit;
4200   }
4201 
4202   Request->ScsiDiskDevice  = ScsiDiskDevice;
4203   Request->Timeout         = Timeout;
4204   Request->TimesRetry      = TimesRetry;
4205   Request->OutBuffer       = DataBuffer;
4206   Request->DataLength      = DataLength;
4207   Request->StartLba        = StartLba;
4208   Request->SectorCount     = SectorCount;
4209   Request->BlkIo2Req       = BlkIo2Req;
4210 
4211   //
4212   // Create Event
4213   //
4214   Status = gBS->CreateEvent (
4215                   EVT_NOTIFY_SIGNAL,
4216                   TPL_NOTIFY,
4217                   ScsiDiskNotify,
4218                   Request,
4219                   &AsyncIoEvent
4220                   );
4221   if (EFI_ERROR(Status)) {
4222     goto ErrorExit;
4223   }
4224 
4225   Status = ScsiWrite16CommandEx (
4226              ScsiDiskDevice->ScsiIo,
4227              Request->Timeout,
4228              Request->SenseData,
4229              &Request->SenseDataLength,
4230              &Request->HostAdapterStatus,
4231              &Request->TargetStatus,
4232              Request->OutBuffer,
4233              &Request->DataLength,
4234              Request->StartLba,
4235              Request->SectorCount,
4236              AsyncIoEvent
4237              );
4238   if (EFI_ERROR(Status)) {
4239     goto ErrorExit;
4240   }
4241 
4242   return EFI_SUCCESS;
4243 
4244 ErrorExit:
4245   if (AsyncIoEvent != NULL) {
4246     gBS->CloseEvent (AsyncIoEvent);
4247   }
4248 
4249   if (Request != NULL) {
4250     if (Request->SenseData != NULL) {
4251       FreePool (Request->SenseData);
4252     }
4253 
4254     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
4255     RemoveEntryList (&Request->Link);
4256     gBS->RestoreTPL (OldTpl);
4257 
4258     FreePool (Request);
4259   }
4260 
4261   return Status;
4262 }
4263 
4264 
4265 /**
4266   Check sense key to find if media presents.
4267 
4268   @param  SenseData   The pointer of EFI_SCSI_SENSE_DATA
4269   @param  SenseCounts The number of sense key
4270 
4271   @retval TRUE    NOT any media
4272   @retval FALSE   Media presents
4273 **/
4274 BOOLEAN
ScsiDiskIsNoMedia(IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN SenseCounts)4275 ScsiDiskIsNoMedia (
4276   IN  EFI_SCSI_SENSE_DATA   *SenseData,
4277   IN  UINTN                 SenseCounts
4278   )
4279 {
4280   EFI_SCSI_SENSE_DATA *SensePtr;
4281   UINTN               Index;
4282   BOOLEAN             IsNoMedia;
4283 
4284   IsNoMedia = FALSE;
4285   SensePtr  = SenseData;
4286 
4287   for (Index = 0; Index < SenseCounts; Index++) {
4288     //
4289     // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),
4290     // Additional Sense Code is ASC_NO_MEDIA (0x3A)
4291     //
4292     if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
4293         (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {
4294       IsNoMedia = TRUE;
4295     }
4296     SensePtr++;
4297   }
4298 
4299   return IsNoMedia;
4300 }
4301 
4302 
4303 /**
4304   Parse sense key.
4305 
4306   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
4307   @param  SenseCounts  The number of sense key
4308 
4309   @retval TRUE   Error
4310   @retval FALSE  NOT error
4311 
4312 **/
4313 BOOLEAN
ScsiDiskIsMediaError(IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN SenseCounts)4314 ScsiDiskIsMediaError (
4315   IN  EFI_SCSI_SENSE_DATA   *SenseData,
4316   IN  UINTN                 SenseCounts
4317   )
4318 {
4319   EFI_SCSI_SENSE_DATA *SensePtr;
4320   UINTN               Index;
4321   BOOLEAN             IsError;
4322 
4323   IsError   = FALSE;
4324   SensePtr  = SenseData;
4325 
4326   for (Index = 0; Index < SenseCounts; Index++) {
4327 
4328     switch (SensePtr->Sense_Key) {
4329 
4330     case EFI_SCSI_SK_MEDIUM_ERROR:
4331       //
4332       // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)
4333       //
4334       switch (SensePtr->Addnl_Sense_Code) {
4335 
4336       //
4337       // fall through
4338       //
4339       case EFI_SCSI_ASC_MEDIA_ERR1:
4340 
4341       //
4342       // fall through
4343       //
4344       case EFI_SCSI_ASC_MEDIA_ERR2:
4345 
4346       //
4347       // fall through
4348       //
4349       case EFI_SCSI_ASC_MEDIA_ERR3:
4350       case EFI_SCSI_ASC_MEDIA_ERR4:
4351         IsError = TRUE;
4352         break;
4353 
4354       default:
4355         break;
4356       }
4357 
4358       break;
4359 
4360     case EFI_SCSI_SK_NOT_READY:
4361       //
4362       // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)
4363       //
4364       switch (SensePtr->Addnl_Sense_Code) {
4365       //
4366       // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)
4367       //
4368       case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:
4369         IsError = TRUE;
4370         break;
4371 
4372       default:
4373         break;
4374       }
4375       break;
4376 
4377     default:
4378       break;
4379     }
4380 
4381     SensePtr++;
4382   }
4383 
4384   return IsError;
4385 }
4386 
4387 
4388 /**
4389   Check sense key to find if hardware error happens.
4390 
4391   @param  SenseData     The pointer of EFI_SCSI_SENSE_DATA
4392   @param  SenseCounts   The number of sense key
4393 
4394   @retval TRUE  Hardware error exits.
4395   @retval FALSE NO error.
4396 
4397 **/
4398 BOOLEAN
ScsiDiskIsHardwareError(IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN SenseCounts)4399 ScsiDiskIsHardwareError (
4400   IN  EFI_SCSI_SENSE_DATA   *SenseData,
4401   IN  UINTN                 SenseCounts
4402   )
4403 {
4404   EFI_SCSI_SENSE_DATA *SensePtr;
4405   UINTN               Index;
4406   BOOLEAN             IsError;
4407 
4408   IsError   = FALSE;
4409   SensePtr  = SenseData;
4410 
4411   for (Index = 0; Index < SenseCounts; Index++) {
4412 
4413     //
4414     // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)
4415     //
4416     if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {
4417       IsError = TRUE;
4418     }
4419 
4420     SensePtr++;
4421   }
4422 
4423   return IsError;
4424 }
4425 
4426 
4427 /**
4428   Check sense key to find if media has changed.
4429 
4430   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
4431   @param  SenseCounts  The number of sense key
4432 
4433   @retval TRUE   Media is changed.
4434   @retval FALSE  Media is NOT changed.
4435 **/
4436 BOOLEAN
ScsiDiskIsMediaChange(IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN SenseCounts)4437 ScsiDiskIsMediaChange (
4438   IN  EFI_SCSI_SENSE_DATA   *SenseData,
4439   IN  UINTN                 SenseCounts
4440   )
4441 {
4442   EFI_SCSI_SENSE_DATA *SensePtr;
4443   UINTN               Index;
4444   BOOLEAN             IsMediaChanged;
4445 
4446   IsMediaChanged  = FALSE;
4447   SensePtr        = SenseData;
4448 
4449   for (Index = 0; Index < SenseCounts; Index++) {
4450     //
4451     // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),
4452     // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)
4453     //
4454     if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
4455         (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {
4456       IsMediaChanged = TRUE;
4457     }
4458 
4459     SensePtr++;
4460   }
4461 
4462   return IsMediaChanged;
4463 }
4464 
4465 /**
4466   Check sense key to find if reset happens.
4467 
4468   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
4469   @param  SenseCounts  The number of sense key
4470 
4471   @retval TRUE  It is reset before.
4472   @retval FALSE It is NOT reset before.
4473 
4474 **/
4475 BOOLEAN
ScsiDiskIsResetBefore(IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN SenseCounts)4476 ScsiDiskIsResetBefore (
4477   IN  EFI_SCSI_SENSE_DATA   *SenseData,
4478   IN  UINTN                 SenseCounts
4479   )
4480 {
4481   EFI_SCSI_SENSE_DATA *SensePtr;
4482   UINTN               Index;
4483   BOOLEAN             IsResetBefore;
4484 
4485   IsResetBefore = FALSE;
4486   SensePtr      = SenseData;
4487 
4488   for (Index = 0; Index < SenseCounts; Index++) {
4489 
4490     //
4491     // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)
4492     // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)
4493     //
4494     if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
4495         (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {
4496       IsResetBefore = TRUE;
4497     }
4498 
4499     SensePtr++;
4500   }
4501 
4502   return IsResetBefore;
4503 }
4504 
4505 /**
4506   Check sense key to find if the drive is ready.
4507 
4508   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
4509   @param  SenseCounts  The number of sense key
4510   @param  RetryLater   The flag means if need a retry
4511 
4512   @retval TRUE  Drive is ready.
4513   @retval FALSE Drive is NOT ready.
4514 
4515 **/
4516 BOOLEAN
ScsiDiskIsDriveReady(IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN SenseCounts,OUT BOOLEAN * RetryLater)4517 ScsiDiskIsDriveReady (
4518   IN  EFI_SCSI_SENSE_DATA   *SenseData,
4519   IN  UINTN                 SenseCounts,
4520   OUT BOOLEAN               *RetryLater
4521   )
4522 {
4523   EFI_SCSI_SENSE_DATA *SensePtr;
4524   UINTN               Index;
4525   BOOLEAN             IsReady;
4526 
4527   IsReady     = TRUE;
4528   *RetryLater = FALSE;
4529   SensePtr    = SenseData;
4530 
4531   for (Index = 0; Index < SenseCounts; Index++) {
4532 
4533     switch (SensePtr->Sense_Key) {
4534 
4535     case EFI_SCSI_SK_NOT_READY:
4536       //
4537       // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)
4538       //
4539       switch (SensePtr->Addnl_Sense_Code) {
4540       case EFI_SCSI_ASC_NOT_READY:
4541         //
4542         // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)
4543         //
4544         switch (SensePtr->Addnl_Sense_Code_Qualifier) {
4545         case EFI_SCSI_ASCQ_IN_PROGRESS:
4546           //
4547           // Additional Sense Code Qualifier is
4548           // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)
4549           //
4550           IsReady     = FALSE;
4551           *RetryLater = TRUE;
4552           break;
4553 
4554         default:
4555           IsReady     = FALSE;
4556           *RetryLater = FALSE;
4557           break;
4558         }
4559         break;
4560 
4561       default:
4562         break;
4563       }
4564       break;
4565 
4566     default:
4567       break;
4568     }
4569 
4570     SensePtr++;
4571   }
4572 
4573   return IsReady;
4574 }
4575 
4576 /**
4577   Check sense key to find if it has sense key.
4578 
4579   @param  SenseData   - The pointer of EFI_SCSI_SENSE_DATA
4580   @param  SenseCounts - The number of sense key
4581 
4582   @retval TRUE  It has sense key.
4583   @retval FALSE It has NOT any sense key.
4584 
4585 **/
4586 BOOLEAN
ScsiDiskHaveSenseKey(IN EFI_SCSI_SENSE_DATA * SenseData,IN UINTN SenseCounts)4587 ScsiDiskHaveSenseKey (
4588   IN  EFI_SCSI_SENSE_DATA   *SenseData,
4589   IN  UINTN                 SenseCounts
4590   )
4591 {
4592   EFI_SCSI_SENSE_DATA *SensePtr;
4593   UINTN               Index;
4594   BOOLEAN             HaveSenseKey;
4595 
4596   if (SenseCounts == 0) {
4597     HaveSenseKey = FALSE;
4598   } else {
4599     HaveSenseKey = TRUE;
4600   }
4601 
4602   SensePtr = SenseData;
4603 
4604   for (Index = 0; Index < SenseCounts; Index++) {
4605 
4606     //
4607     // Sense Key is SK_NO_SENSE (0x0)
4608     //
4609     if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&
4610         (Index == 0)) {
4611       HaveSenseKey = FALSE;
4612     }
4613 
4614     SensePtr++;
4615   }
4616 
4617   return HaveSenseKey;
4618 }
4619 
4620 /**
4621   Release resource about disk device.
4622 
4623   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
4624 
4625 **/
4626 VOID
ReleaseScsiDiskDeviceResources(IN SCSI_DISK_DEV * ScsiDiskDevice)4627 ReleaseScsiDiskDeviceResources (
4628   IN  SCSI_DISK_DEV   *ScsiDiskDevice
4629   )
4630 {
4631   if (ScsiDiskDevice == NULL) {
4632     return ;
4633   }
4634 
4635   if (ScsiDiskDevice->SenseData != NULL) {
4636     FreePool (ScsiDiskDevice->SenseData);
4637     ScsiDiskDevice->SenseData = NULL;
4638   }
4639 
4640   if (ScsiDiskDevice->ControllerNameTable != NULL) {
4641     FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);
4642     ScsiDiskDevice->ControllerNameTable = NULL;
4643   }
4644 
4645   FreePool (ScsiDiskDevice);
4646 
4647   ScsiDiskDevice = NULL;
4648 }
4649 
4650 /**
4651   Determine if Block Io & Block Io2 should be produced.
4652 
4653 
4654   @param  ChildHandle  Child Handle to retrieve Parent information.
4655 
4656   @retval  TRUE    Should produce Block Io & Block Io2.
4657   @retval  FALSE   Should not produce Block Io & Block Io2.
4658 
4659 **/
4660 BOOLEAN
DetermineInstallBlockIo(IN EFI_HANDLE ChildHandle)4661 DetermineInstallBlockIo (
4662   IN  EFI_HANDLE      ChildHandle
4663   )
4664 {
4665   EFI_SCSI_PASS_THRU_PROTOCOL           *ScsiPassThru;
4666   EFI_EXT_SCSI_PASS_THRU_PROTOCOL       *ExtScsiPassThru;
4667 
4668   //
4669   // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence,
4670   // check its attribute, logic or physical.
4671   //
4672   ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle);
4673   if (ExtScsiPassThru != NULL) {
4674     if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {
4675       return TRUE;
4676     }
4677   }
4678 
4679   //
4680   // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence,
4681   // check its attribute, logic or physical.
4682   //
4683   ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle);
4684   if (ScsiPassThru != NULL) {
4685     if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {
4686       return TRUE;
4687     }
4688   }
4689 
4690   return FALSE;
4691 }
4692 
4693 /**
4694   Search protocol database and check to see if the protocol
4695   specified by ProtocolGuid is present on a ControllerHandle and opened by
4696   ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
4697   If the ControllerHandle is found, then the protocol specified by ProtocolGuid
4698   will be opened on it.
4699 
4700 
4701   @param  ProtocolGuid   ProtocolGuid pointer.
4702   @param  ChildHandle    Child Handle to retrieve Parent information.
4703 
4704 **/
4705 VOID *
4706 EFIAPI
GetParentProtocol(IN EFI_GUID * ProtocolGuid,IN EFI_HANDLE ChildHandle)4707 GetParentProtocol (
4708   IN  EFI_GUID                          *ProtocolGuid,
4709   IN  EFI_HANDLE                        ChildHandle
4710   )
4711 {
4712   UINTN                                 Index;
4713   UINTN                                 HandleCount;
4714   VOID                                  *Interface;
4715   EFI_STATUS                            Status;
4716   EFI_HANDLE                            *HandleBuffer;
4717 
4718   //
4719   // Retrieve the list of all handles from the handle database
4720   //
4721   Status = gBS->LocateHandleBuffer (
4722                   ByProtocol,
4723                   ProtocolGuid,
4724                   NULL,
4725                   &HandleCount,
4726                   &HandleBuffer
4727                   );
4728 
4729   if (EFI_ERROR (Status)) {
4730     return NULL;
4731   }
4732 
4733   //
4734   // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle
4735   //
4736   for (Index = 0; Index < HandleCount; Index++) {
4737     Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid);
4738     if (!EFI_ERROR (Status)) {
4739       Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface);
4740       if (!EFI_ERROR (Status)) {
4741         gBS->FreePool (HandleBuffer);
4742         return Interface;
4743       }
4744     }
4745   }
4746 
4747   gBS->FreePool (HandleBuffer);
4748   return NULL;
4749 }
4750 
4751 /**
4752   Provides inquiry information for the controller type.
4753 
4754   This function is used by the IDE bus driver to get inquiry data.  Data format
4755   of Identify data is defined by the Interface GUID.
4756 
4757   @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL instance.
4758   @param[in, out] InquiryData       Pointer to a buffer for the inquiry data.
4759   @param[in, out] InquiryDataSize   Pointer to the value for the inquiry data size.
4760 
4761   @retval EFI_SUCCESS            The command was accepted without any errors.
4762   @retval EFI_NOT_FOUND          Device does not support this data class
4763   @retval EFI_DEVICE_ERROR       Error reading InquiryData from device
4764   @retval EFI_BUFFER_TOO_SMALL   InquiryDataSize not big enough
4765 
4766 **/
4767 EFI_STATUS
4768 EFIAPI
ScsiDiskInfoInquiry(IN EFI_DISK_INFO_PROTOCOL * This,IN OUT VOID * InquiryData,IN OUT UINT32 * InquiryDataSize)4769 ScsiDiskInfoInquiry (
4770   IN     EFI_DISK_INFO_PROTOCOL   *This,
4771   IN OUT VOID                     *InquiryData,
4772   IN OUT UINT32                   *InquiryDataSize
4773   )
4774 {
4775   EFI_STATUS      Status;
4776   SCSI_DISK_DEV   *ScsiDiskDevice;
4777 
4778   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_DISKINFO (This);
4779 
4780   Status = EFI_BUFFER_TOO_SMALL;
4781   if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {
4782     Status = EFI_SUCCESS;
4783     CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));
4784   }
4785   *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);
4786   return Status;
4787 }
4788 
4789 
4790 /**
4791   Provides identify information for the controller type.
4792 
4793   This function is used by the IDE bus driver to get identify data.  Data format
4794   of Identify data is defined by the Interface GUID.
4795 
4796   @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL
4797                                     instance.
4798   @param[in, out] IdentifyData      Pointer to a buffer for the identify data.
4799   @param[in, out] IdentifyDataSize  Pointer to the value for the identify data
4800                                     size.
4801 
4802   @retval EFI_SUCCESS            The command was accepted without any errors.
4803   @retval EFI_NOT_FOUND          Device does not support this data class
4804   @retval EFI_DEVICE_ERROR       Error reading IdentifyData from device
4805   @retval EFI_BUFFER_TOO_SMALL   IdentifyDataSize not big enough
4806 
4807 **/
4808 EFI_STATUS
4809 EFIAPI
ScsiDiskInfoIdentify(IN EFI_DISK_INFO_PROTOCOL * This,IN OUT VOID * IdentifyData,IN OUT UINT32 * IdentifyDataSize)4810 ScsiDiskInfoIdentify (
4811   IN     EFI_DISK_INFO_PROTOCOL   *This,
4812   IN OUT VOID                     *IdentifyData,
4813   IN OUT UINT32                   *IdentifyDataSize
4814   )
4815 {
4816   EFI_STATUS      Status;
4817   SCSI_DISK_DEV   *ScsiDiskDevice;
4818 
4819   if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {
4820     //
4821     // Physical SCSI bus does not support this data class.
4822     //
4823     return EFI_NOT_FOUND;
4824   }
4825 
4826   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_DISKINFO (This);
4827 
4828   Status = EFI_BUFFER_TOO_SMALL;
4829   if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) {
4830     Status = EFI_SUCCESS;
4831     CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData));
4832   }
4833   *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);
4834   return Status;
4835 }
4836 
4837 /**
4838   Provides sense data information for the controller type.
4839 
4840   This function is used by the IDE bus driver to get sense data.
4841   Data format of Sense data is defined by the Interface GUID.
4842 
4843   @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL instance.
4844   @param[in, out] SenseData         Pointer to the SenseData.
4845   @param[in, out] SenseDataSize     Size of SenseData in bytes.
4846   @param[out]     SenseDataNumber   Pointer to the value for the sense data size.
4847 
4848   @retval EFI_SUCCESS            The command was accepted without any errors.
4849   @retval EFI_NOT_FOUND          Device does not support this data class.
4850   @retval EFI_DEVICE_ERROR       Error reading SenseData from device.
4851   @retval EFI_BUFFER_TOO_SMALL   SenseDataSize not big enough.
4852 
4853 **/
4854 EFI_STATUS
4855 EFIAPI
ScsiDiskInfoSenseData(IN EFI_DISK_INFO_PROTOCOL * This,IN OUT VOID * SenseData,IN OUT UINT32 * SenseDataSize,OUT UINT8 * SenseDataNumber)4856 ScsiDiskInfoSenseData (
4857   IN     EFI_DISK_INFO_PROTOCOL   *This,
4858   IN OUT VOID                     *SenseData,
4859   IN OUT UINT32                   *SenseDataSize,
4860   OUT    UINT8                    *SenseDataNumber
4861   )
4862 {
4863   return EFI_NOT_FOUND;
4864 }
4865 
4866 
4867 /**
4868   This function is used by the IDE bus driver to get controller information.
4869 
4870   @param[in]  This         Pointer to the EFI_DISK_INFO_PROTOCOL instance.
4871   @param[out] IdeChannel   Pointer to the Ide Channel number.  Primary or secondary.
4872   @param[out] IdeDevice    Pointer to the Ide Device number.  Master or slave.
4873 
4874   @retval EFI_SUCCESS       IdeChannel and IdeDevice are valid.
4875   @retval EFI_UNSUPPORTED   This is not an IDE device.
4876 
4877 **/
4878 EFI_STATUS
4879 EFIAPI
ScsiDiskInfoWhichIde(IN EFI_DISK_INFO_PROTOCOL * This,OUT UINT32 * IdeChannel,OUT UINT32 * IdeDevice)4880 ScsiDiskInfoWhichIde (
4881   IN  EFI_DISK_INFO_PROTOCOL   *This,
4882   OUT UINT32                   *IdeChannel,
4883   OUT UINT32                   *IdeDevice
4884   )
4885 {
4886   SCSI_DISK_DEV   *ScsiDiskDevice;
4887 
4888   if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {
4889     //
4890     // This is not an IDE physical device.
4891     //
4892     return EFI_UNSUPPORTED;
4893   }
4894 
4895   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_DISKINFO (This);
4896   *IdeChannel     = ScsiDiskDevice->Channel;
4897   *IdeDevice      = ScsiDiskDevice->Device;
4898 
4899   return EFI_SUCCESS;
4900 }
4901 
4902 
4903 /**
4904   Issues ATA IDENTIFY DEVICE command to identify ATAPI device.
4905 
4906   This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to
4907   implement Identify() interface for DiskInfo protocol. The ATA command is sent
4908   via SCSI Request Packet.
4909 
4910   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
4911 
4912   @retval EFI_SUCCESS     The ATAPI device identify data were retrieved successfully.
4913   @retval others          Some error occurred during the identification that ATAPI device.
4914 
4915 **/
4916 EFI_STATUS
AtapiIdentifyDevice(IN OUT SCSI_DISK_DEV * ScsiDiskDevice)4917 AtapiIdentifyDevice (
4918   IN OUT SCSI_DISK_DEV   *ScsiDiskDevice
4919   )
4920 {
4921   EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
4922   UINT8                           Cdb[6];
4923 
4924   //
4925   // Initialize SCSI REQUEST_PACKET and 6-byte Cdb
4926   //
4927   ZeroMem (&CommandPacket, sizeof (CommandPacket));
4928   ZeroMem (Cdb, sizeof (Cdb));
4929 
4930   Cdb[0] = ATA_CMD_IDENTIFY_DEVICE;
4931   CommandPacket.Timeout = SCSI_DISK_TIMEOUT;
4932   CommandPacket.Cdb = Cdb;
4933   CommandPacket.CdbLength = (UINT8) sizeof (Cdb);
4934   CommandPacket.InDataBuffer = &ScsiDiskDevice->IdentifyData;
4935   CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData);
4936 
4937   return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);
4938 }
4939 
4940 
4941 /**
4942   Initialize the installation of DiskInfo protocol.
4943 
4944   This function prepares for the installation of DiskInfo protocol on the child handle.
4945   By default, it installs DiskInfo protocol with SCSI interface GUID. If it further
4946   detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID
4947   to be IDE/AHCI interface GUID.
4948 
4949   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV.
4950   @param  ChildHandle     Child handle to install DiskInfo protocol.
4951 
4952 **/
4953 VOID
InitializeInstallDiskInfo(IN SCSI_DISK_DEV * ScsiDiskDevice,IN EFI_HANDLE ChildHandle)4954 InitializeInstallDiskInfo (
4955   IN  SCSI_DISK_DEV   *ScsiDiskDevice,
4956   IN  EFI_HANDLE      ChildHandle
4957   )
4958 {
4959   EFI_STATUS                Status;
4960   EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;
4961   EFI_DEVICE_PATH_PROTOCOL  *ChildDevicePathNode;
4962   ATAPI_DEVICE_PATH         *AtapiDevicePath;
4963   SATA_DEVICE_PATH          *SataDevicePath;
4964   UINTN                     IdentifyRetry;
4965 
4966   Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePathNode);
4967   //
4968   // Device Path protocol must be installed on the device handle.
4969   //
4970   ASSERT_EFI_ERROR (Status);
4971   //
4972   // Copy the DiskInfo protocol template.
4973   //
4974   CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate));
4975 
4976   while (!IsDevicePathEnd (DevicePathNode)) {
4977     ChildDevicePathNode = NextDevicePathNode (DevicePathNode);
4978     if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&
4979         (DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&
4980         (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&
4981        ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) ||
4982         (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP))) {
4983 
4984       IdentifyRetry = 3;
4985       do {
4986         //
4987         // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol
4988         // with IDE/AHCI interface GUID.
4989         //
4990         Status = AtapiIdentifyDevice (ScsiDiskDevice);
4991         if (!EFI_ERROR (Status)) {
4992           if (DevicePathSubType(ChildDevicePathNode) == MSG_ATAPI_DP) {
4993             //
4994             // We find the valid ATAPI device path
4995             //
4996             AtapiDevicePath = (ATAPI_DEVICE_PATH *) ChildDevicePathNode;
4997             ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary;
4998             ScsiDiskDevice->Device = AtapiDevicePath->SlaveMaster;
4999             //
5000             // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device.
5001             //
5002             CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid);
5003           } else {
5004             //
5005             // We find the valid SATA device path
5006             //
5007             SataDevicePath = (SATA_DEVICE_PATH *) ChildDevicePathNode;
5008             ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber;
5009             ScsiDiskDevice->Device = SataDevicePath->PortMultiplierPortNumber;
5010             //
5011             // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device.
5012             //
5013             CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid);
5014           }
5015           return;
5016         }
5017       } while (--IdentifyRetry > 0);
5018     } else if ((DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&
5019        (DevicePathSubType (ChildDevicePathNode) == MSG_UFS_DP)) {
5020       CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoUfsInterfaceGuid);
5021       break;
5022     }
5023     DevicePathNode = ChildDevicePathNode;
5024   }
5025 
5026   return;
5027 }
5028