+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+ goto Done;\r
+ }\r
+ }\r
+ //\r
+ // Get the intrinsic block size\r
+ //\r
+ BlockSize = Media->BlockSize;\r
+\r
+ NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaId != Media->MediaId) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Buffer == NULL) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize == 0) {\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize % BlockSize != 0) {\r
+ Status = EFI_BAD_BUFFER_SIZE;\r
+ goto Done;\r
+ }\r
+\r
+ if (Lba > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // If all the parameters are valid, then perform read sectors command\r
+ // to transfer data from device to host.\r
+ //\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ Status = ScsiDiskAsyncReadSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskReadSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks\r
+ );\r
+ }\r
+\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The function is to Write Block to SCSI Disk.\r
+\r
+ @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
+ @param MediaId The Id of Media detected.\r
+ @param Lba The logic block address.\r
+ @param Token A pointer to the token associated with the transaction.\r
+ @param BufferSize The size of Buffer.\r
+ @param Buffer The buffer to fill the read out data.\r
+\r
+ @retval EFI_SUCCESS The data were written correctly to the device.\r
+ @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
+ to perform the write operation.\r
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
+ the intrinsic block size of the device.\r
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not\r
+ valid, or the buffer is not on proper\r
+ alignment.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskWriteBlocksEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
+\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+ goto Done;\r
+ }\r
+ }\r
+ //\r
+ // Get the intrinsic block size\r
+ //\r
+ BlockSize = Media->BlockSize;\r
+\r
+ NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaId != Media->MediaId) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize == 0) {\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
+ if (Buffer == NULL) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize % BlockSize != 0) {\r
+ Status = EFI_BAD_BUFFER_SIZE;\r
+ goto Done;\r
+ }\r
+\r
+ if (Lba > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // if all the parameters are valid, then perform write sectors command\r
+ // to transfer data from device to host.\r
+ //\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ Status = ScsiDiskAsyncWriteSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskWriteSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks\r
+ );\r
+ }\r
+\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Flush the Block Device.\r
+\r
+ @param This Indicates a pointer to the calling context.\r
+ @param Token A pointer to the token associated with the transaction.\r
+\r
+ @retval EFI_SUCCESS All outstanding data was written to the device.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting to\r
+ write data.\r
+ @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskFlushBlocksEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
+\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Wait for the BlockIo2 requests queue to become empty\r
+ //\r
+ while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue));\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Signal caller event\r
+ //\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Internal helper notify function which process the result of an asynchronous\r
+ SCSI UNMAP Command and signal the event passed from EraseBlocks.\r
+\r
+ @param Event The instance of EFI_EVENT.\r
+ @param Context The parameter passed in.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ScsiDiskAsyncUnmapNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ SCSI_ERASEBLK_REQUEST *EraseBlkReq;\r
+ EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket;\r
+ EFI_ERASE_BLOCK_TOKEN *Token;\r
+ EFI_STATUS Status;\r
+\r
+ gBS->CloseEvent (Event);\r
+\r
+ EraseBlkReq = (SCSI_ERASEBLK_REQUEST *) Context;\r
+ CommandPacket = &EraseBlkReq->CommandPacket;\r
+ Token = EraseBlkReq->Token;\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+\r
+ Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "ScsiDiskAsyncUnmapNotify: Host adapter indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ Token->TransactionStatus = Status;\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "ScsiDiskAsyncUnmapNotify: Target indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ Token->TransactionStatus = Status;\r
+ goto Done;\r
+ }\r
+\r
+Done:\r
+ RemoveEntryList (&EraseBlkReq->Link);\r
+ FreePool (CommandPacket->OutDataBuffer);\r
+ FreePool (EraseBlkReq->CommandPacket.Cdb);\r
+ FreePool (EraseBlkReq);\r
+\r
+ gBS->SignalEvent (Token->Event);\r
+}\r
+\r
+/**\r
+ Require the device server to cause one or more LBAs to be unmapped.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
+ @param Lba The start block number.\r
+ @param Blocks Total block number to be unmapped.\r
+ @param Token The pointer to the token associated with the\r
+ non-blocking erase block request.\r
+\r
+ @retval EFI_SUCCESS Target blocks have been successfully unmapped.\r
+ @retval EFI_DEVICE_ERROR Fail to unmap the target blocks.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskUnmap (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN UINT64 Lba,\r
+ IN UINTN Blocks,\r
+ IN EFI_ERASE_BLOCK_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
+ SCSI_ERASEBLK_REQUEST *EraseBlkReq;\r
+ EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket;\r
+ EFI_SCSI_DISK_UNMAP_BLOCK_DESP *BlkDespPtr;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ UINT8 *Cdb;\r
+ UINT32 MaxLbaCnt;\r
+ UINT32 MaxBlkDespCnt;\r
+ UINT32 BlkDespCnt;\r
+ UINT16 UnmapParamListLen;\r
+ VOID *UnmapParamList;\r
+ EFI_EVENT AsyncUnmapEvent;\r
+ EFI_TPL OldTpl;\r
+\r
+ ScsiIo = ScsiDiskDevice->ScsiIo;\r
+ MaxLbaCnt = ScsiDiskDevice->UnmapInfo.MaxLbaCnt;\r
+ MaxBlkDespCnt = ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt;\r
+ EraseBlkReq = NULL;\r
+ UnmapParamList = NULL;\r
+ AsyncUnmapEvent = NULL;\r
+ ReturnStatus = EFI_SUCCESS;\r
+\r
+ if (Blocks / (UINTN) MaxLbaCnt > MaxBlkDespCnt) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ EraseBlkReq = AllocateZeroPool (sizeof (SCSI_ERASEBLK_REQUEST));\r
+ if (EraseBlkReq == NULL) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ EraseBlkReq->CommandPacket.Cdb = AllocateZeroPool (0xA);\r
+ if (EraseBlkReq->CommandPacket.Cdb == NULL) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ BlkDespCnt = (UINT32) ((Blocks - 1) / MaxLbaCnt + 1);\r
+ UnmapParamListLen = (UINT16) (sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER)\r
+ + BlkDespCnt * sizeof (EFI_SCSI_DISK_UNMAP_BLOCK_DESP));\r
+ UnmapParamList = AllocateZeroPool (UnmapParamListLen);\r
+ if (UnmapParamList == NULL) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ *((UINT16 *)UnmapParamList) = SwapBytes16 (UnmapParamListLen - 2);\r
+ *((UINT16 *)UnmapParamList + 1) = SwapBytes16 (UnmapParamListLen - sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
+\r
+ BlkDespPtr = (EFI_SCSI_DISK_UNMAP_BLOCK_DESP *)((UINT8 *)UnmapParamList + sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
+ while (Blocks > 0) {\r
+ if (Blocks > MaxLbaCnt) {\r
+ *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba);\r
+ *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 (MaxLbaCnt);\r
+ Blocks -= MaxLbaCnt;\r
+ Lba += MaxLbaCnt;\r
+ } else {\r
+ *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba);\r
+ *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 ((UINT32) Blocks);\r
+ Blocks = 0;\r
+ }\r
+\r
+ BlkDespPtr++;\r
+ }\r
+\r
+ CommandPacket = &EraseBlkReq->CommandPacket;\r
+ CommandPacket->Timeout = SCSI_DISK_TIMEOUT;\r
+ CommandPacket->OutDataBuffer = UnmapParamList;\r
+ CommandPacket->OutTransferLength = UnmapParamListLen;\r
+ CommandPacket->CdbLength = 0xA;\r
+ CommandPacket->DataDirection = EFI_SCSI_DATA_OUT;\r
+ //\r
+ // Fill Cdb for UNMAP Command\r
+ //\r
+ Cdb = CommandPacket->Cdb;\r
+ Cdb[0] = EFI_SCSI_OP_UNMAP;\r
+ WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 (UnmapParamListLen));\r
+\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ //\r
+ // Non-blocking UNMAP request\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ ScsiDiskAsyncUnmapNotify,\r
+ EraseBlkReq,\r
+ &AsyncUnmapEvent\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &EraseBlkReq->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ EraseBlkReq->Token = Token;\r
+\r
+ Status = ScsiIo->ExecuteScsiCommand (\r
+ ScsiIo,\r
+ CommandPacket,\r
+ AsyncUnmapEvent\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ RemoveEntryList (&EraseBlkReq->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ goto Done;\r
+ } else {\r
+ //\r
+ // Directly return if the non-blocking UNMAP request is queued.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+ } else {\r
+ //\r
+ // Blocking UNMAP request\r
+ //\r
+ Status = ScsiIo->ExecuteScsiCommand (\r
+ ScsiIo,\r
+ CommandPacket,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Only blocking UNMAP request will reach here.\r
+ //\r
+ Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "ScsiDiskUnmap: Host adapter indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "ScsiDiskUnmap: Target indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+Done:\r
+ if (EraseBlkReq != NULL) {\r
+ if (EraseBlkReq->CommandPacket.Cdb != NULL) {\r
+ FreePool (EraseBlkReq->CommandPacket.Cdb);\r
+ }\r
+ FreePool (EraseBlkReq);\r
+ }\r
+\r
+ if (UnmapParamList != NULL) {\r
+ FreePool (UnmapParamList);\r
+ }\r
+\r
+ if (AsyncUnmapEvent != NULL) {\r
+ gBS->CloseEvent (AsyncUnmapEvent);\r
+ }\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+/**\r
+ Erase a specified number of device blocks.\r
+\r
+ @param[in] This Indicates a pointer to the calling context.\r
+ @param[in] MediaId The media ID that the erase request is for.\r
+ @param[in] Lba The starting logical block address to be\r
+ erased. The caller is responsible for erasing\r
+ only legitimate locations.\r
+ @param[in, out] Token A pointer to the token associated with the\r
+ transaction.\r
+ @param[in] Size The size in bytes to be erased. This must be\r
+ a multiple of the physical block size of the\r
+ device.\r
+\r
+ @retval EFI_SUCCESS The erase request was queued if Event is not\r
+ NULL. The data was erased correctly to the\r
+ device if the Event is NULL.to the device.\r
+ @retval EFI_WRITE_PROTECTED The device cannot be erased due to write\r
+ protection.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
+ to perform the erase operation.\r
+ @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not\r
+ valid.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskEraseBlocks (\r
+ IN EFI_ERASE_BLOCK_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN OUT EFI_ERASE_BLOCK_TOKEN *Token,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
+\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_ERASEBLK (This);\r
+\r
+ if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+ }\r
+ //\r
+ // Get the intrinsic block size\r
+ //\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaId != Media->MediaId) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Size == 0) {\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
+ BlockSize = Media->BlockSize;\r
+ if ((Size % BlockSize) != 0) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ NumberOfBlocks = Size / BlockSize;\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, Token);\r
+ } else {\r
+ Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, NULL);\r
+ }\r
+\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Detect Device and read out capacity ,if error occurs, parse the sense key.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param MustReadCapacity The flag about reading device capacity\r
+ @param MediaChange The pointer of flag indicates if media has changed\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates that error occurs\r
+ @retval EFI_SUCCESS Successfully to detect media\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskDetectMedia (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN BOOLEAN MustReadCapacity,\r
+ OUT BOOLEAN *MediaChange\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SCSI_SENSE_DATA *SenseData;\r
+ UINTN NumberOfSenseKeys;\r
+ BOOLEAN NeedRetry;\r
+ BOOLEAN NeedReadCapacity;\r
+ UINT8 Retry;\r
+ UINT8 MaxRetry;\r
+ EFI_BLOCK_IO_MEDIA OldMedia;\r
+ UINTN Action;\r
+ EFI_EVENT TimeoutEvt;\r
+\r
+ Status = EFI_SUCCESS;\r
+ SenseData = NULL;\r
+ NumberOfSenseKeys = 0;\r
+ Retry = 0;\r
+ MaxRetry = 3;\r
+ Action = ACTION_NO_ACTION;\r
+ NeedReadCapacity = FALSE;\r
+ *MediaChange = FALSE;\r
+ TimeoutEvt = NULL;\r
+\r
+ CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &TimeoutEvt\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS(120));\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Sending Test_Unit cmd to poll device status.\r
+ // If the sense data shows the drive is not ready or reset before, we need poll the device status again.\r
+ // We limit the upper boundary to 120 seconds.\r
+ //\r
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {\r
+ Status = ScsiDiskTestUnitReady (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ &SenseData,\r
+ &NumberOfSenseKeys\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = DetectMediaParsingSenseKeys (\r
+ ScsiDiskDevice,\r
+ SenseData,\r
+ NumberOfSenseKeys,\r
+ &Action\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ continue;\r
+ } else {\r
+ break;\r
+ }\r
+ } else {\r
+ Retry++;\r
+ if (!NeedRetry || (Retry >= MaxRetry)) {\r
+ goto EXIT;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // ACTION_NO_ACTION: need not read capacity\r
+ // other action code: need read capacity\r
+ //\r
+ if (Action == ACTION_READ_CAPACITY) {\r
+ NeedReadCapacity = TRUE;\r
+ }\r
+\r
+ //\r
+ // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
+ // retrieve capacity via Read Capacity command\r
+ //\r
+ if (NeedReadCapacity || MustReadCapacity) {\r
+ //\r
+ // retrieve media information\r
+ //\r
+ for (Retry = 0; Retry < MaxRetry; Retry++) {\r
+ Status = ScsiDiskReadCapacity (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ &SenseData,\r
+ &NumberOfSenseKeys\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // analyze sense key to action\r
+ //\r
+ Status = DetectMediaParsingSenseKeys (\r
+ ScsiDiskDevice,\r
+ SenseData,\r
+ NumberOfSenseKeys,\r
+ &Action\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // if Status is error, it may indicate crisis error,\r
+ // so return without retry.\r
+ //\r
+ goto EXIT;\r
+ } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ Retry = 0;\r
+ continue;\r
+ } else {\r
+ break;\r
+ }\r
+ } else {\r
+ Retry++;\r
+ if (!NeedRetry || (Retry >= MaxRetry)) {\r
+ goto EXIT;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
+ //\r
+ // Media change information got from the device\r
+ //\r
+ *MediaChange = TRUE;\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
+ *MediaChange = TRUE;\r
+ ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
+ *MediaChange = TRUE;\r
+ ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ }\r