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