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