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