+/**\r
+ Probe the media status and return EFI_NO_MEDIA or EFI_MEDIA_CHANGED\r
+ for no media or media change case. Otherwise DefaultStatus is returned.\r
+\r
+ @param DiskIo2 Pointer to the DiskIo2 instance.\r
+ @param MediaId Id of the media, changes every time the media is replaced.\r
+ @param DefaultStatus The default status to return when it's not the no media\r
+ or media change case.\r
+\r
+ @retval EFI_NO_MEDIA There is no media.\r
+ @retval EFI_MEDIA_CHANGED The media was changed.\r
+ @retval others The default status to return.\r
+**/\r
+EFI_STATUS\r
+ProbeMediaStatusEx (\r
+ IN EFI_DISK_IO2_PROTOCOL *DiskIo2,\r
+ IN UINT32 MediaId,\r
+ IN EFI_STATUS DefaultStatus\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 Buffer[1];\r
+\r
+ //\r
+ // Read 1 byte from offset 0 to check if the MediaId is still valid.\r
+ // The reading operation is synchronious thus it is not worth it to\r
+ // allocate a buffer from the pool. The destination buffer for the\r
+ // data is in the stack.\r
+ //\r
+ Status = DiskIo2->ReadDiskEx (DiskIo2, MediaId, 0, NULL, 1, (VOID*)Buffer);\r
+ if ((Status == EFI_NO_MEDIA) || (Status == EFI_MEDIA_CHANGED)) {\r
+ return Status;\r
+ }\r
+ return DefaultStatus;\r
+}\r
+\r
+/**\r
+ Reset the Block Device throught Block I/O2 protocol.\r
+\r
+ @param This Protocol instance pointer.\r
+ @param ExtendedVerification Driver may perform diagnostics on reset.\r
+\r
+ @retval EFI_SUCCESS The device was reset.\r
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
+ not be reset.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PartitionResetEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
+ )\r
+{\r
+ PARTITION_PRIVATE_DATA *Private;\r
+\r
+ Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This);\r
+\r
+ return Private->ParentBlockIo2->Reset (\r
+ Private->ParentBlockIo2,\r
+ ExtendedVerification\r
+ );\r
+}\r
+\r
+/**\r
+ The general callback for the DiskIo2 interfaces.\r
+ @param Event Event whose notification function is being invoked.\r
+ @param Context The pointer to the notification function's context,\r
+ which points to the PARTITION_ACCESS_TASK instance.\r
+**/\r
+VOID\r
+EFIAPI\r
+PartitionOnAccessComplete (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ PARTITION_ACCESS_TASK *Task;\r
+\r
+ Task = (PARTITION_ACCESS_TASK *) Context;\r
+\r
+ gBS->CloseEvent (Event);\r
+\r
+ Task->BlockIo2Token->TransactionStatus = Task->DiskIo2Token.TransactionStatus;\r
+ gBS->SignalEvent (Task->BlockIo2Token->Event);\r
+\r
+ FreePool (Task);\r
+}\r
+\r
+/**\r
+ Create a new PARTITION_ACCESS_TASK instance.\r
+\r
+ @param Token Pointer to the EFI_BLOCK_IO2_TOKEN.\r
+\r
+ @return Pointer to the created PARTITION_ACCESS_TASK instance or NULL upon failure.\r
+**/\r
+PARTITION_ACCESS_TASK *\r
+PartitionCreateAccessTask (\r
+ IN EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PARTITION_ACCESS_TASK *Task;\r
+\r
+ Task = AllocatePool (sizeof (*Task));\r
+ if (Task == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PartitionOnAccessComplete,\r
+ Task,\r
+ &Task->DiskIo2Token.Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Task);\r
+ return NULL;\r
+ }\r
+\r
+ Task->BlockIo2Token = Token;\r
+\r
+ return Task;\r
+}\r
+\r
+/**\r
+ Read BufferSize bytes from Lba into Buffer.\r
+\r
+ This function reads the requested number of blocks from the device. All the\r
+ blocks are read, or an error is returned.\r
+ If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and\r
+ non-blocking I/O is being used, the Event associated with this request will\r
+ not be signaled.\r
+\r
+ @param[in] This Indicates a pointer to the calling context.\r
+ @param[in] MediaId Id of the media, changes every time the media is\r
+ replaced.\r
+ @param[in] Lba The starting Logical Block Address to read from.\r
+ @param[in, out] Token A pointer to the token associated with the transaction.\r
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.\r
+ @param[out] Buffer A pointer to the destination buffer for the data. The\r
+ caller is responsible for either having implicit or\r
+ explicit ownership of the buffer.\r
+\r
+ @retval EFI_SUCCESS The read request was queued if Token->Event is\r
+ not NULL.The data was read correctly from the\r
+ device if the Token->Event is NULL.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while performing\r
+ the read.\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_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the\r
+ intrinsic block size of the device.\r
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,\r
+ or the buffer is not on proper alignment.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack\r
+ of resources.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PartitionReadBlocksEx (\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
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PARTITION_PRIVATE_DATA *Private;\r
+ UINT64 Offset;\r
+ PARTITION_ACCESS_TASK *Task;\r
+\r
+ Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This);\r
+\r
+ if (BufferSize % Private->BlockSize != 0) {\r
+ return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_BAD_BUFFER_SIZE);\r
+ }\r
+\r
+ Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start;\r
+ if (Offset + BufferSize > Private->End) {\r
+ return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_INVALID_PARAMETER);\r
+ }\r
+\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Task = PartitionCreateAccessTask (Token);\r
+ if (Task == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Private->DiskIo2->ReadDiskEx (Private->DiskIo2, MediaId, Offset, &Task->DiskIo2Token, BufferSize, Buffer);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->CloseEvent (Task->DiskIo2Token.Event);\r
+ FreePool (Task);\r
+ }\r
+ } else {\r
+ Status = Private->DiskIo2->ReadDiskEx (Private->DiskIo2, MediaId, Offset, NULL, BufferSize, Buffer);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Write BufferSize bytes from Lba into Buffer.\r
+\r
+ This function writes the requested number of blocks to the device. All blocks\r
+ are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,\r
+ EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is\r
+ being used, the Event associated with this request will not be signaled.\r
+\r
+ @param[in] This Indicates a pointer to the calling context.\r
+ @param[in] MediaId The media ID that the write request is for.\r
+ @param[in] Lba The starting logical block address to be written. The\r
+ caller is responsible for writing to only legitimate\r
+ locations.\r
+ @param[in, out] Token A pointer to the token associated with the transaction.\r
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.\r
+ @param[in] Buffer A pointer to the source buffer for the data.\r
+\r
+ @retval EFI_SUCCESS The write request was queued if Event is not NULL.\r
+ The data was written correctly to the device if\r
+ the Event is NULL.\r
+ @retval EFI_WRITE_PROTECTED The device can not be written to.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the write.\r
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,\r
+ or the buffer is not on proper alignment.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack\r
+ of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PartitionWriteBlocksEx (\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
+ EFI_STATUS Status;\r
+ PARTITION_PRIVATE_DATA *Private;\r
+ UINT64 Offset;\r
+ PARTITION_ACCESS_TASK *Task;\r
+\r
+ Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This);\r
+\r
+ if (BufferSize % Private->BlockSize != 0) {\r
+ return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_BAD_BUFFER_SIZE);\r
+ }\r
+\r
+ Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start;\r
+ if (Offset + BufferSize > Private->End) {\r
+ return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_INVALID_PARAMETER);\r
+ }\r
+\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Task = PartitionCreateAccessTask (Token);\r
+ if (Task == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Private->DiskIo2->WriteDiskEx (Private->DiskIo2, MediaId, Offset, &Task->DiskIo2Token, BufferSize, Buffer);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->CloseEvent (Task->DiskIo2Token.Event);\r
+ FreePool (Task);\r
+ }\r
+ } else {\r
+ Status = Private->DiskIo2->WriteDiskEx (Private->DiskIo2, MediaId, Offset, NULL, BufferSize, Buffer);\r
+ }\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Flush the Block Device.\r
+\r
+ If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED\r
+ is returned and non-blocking I/O is being used, the Event associated with\r
+ this request will not be signaled.\r
+\r
+ @param[in] This Indicates a pointer to the calling context.\r
+ @param[in, out] Token A pointer to the token associated with the transaction\r
+\r
+ @retval EFI_SUCCESS The flush request was queued if Event is not NULL.\r
+ All outstanding data was written correctly to the\r
+ device if the Event is NULL.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while writting back\r
+ the 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
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack\r
+ of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PartitionFlushBlocksEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PARTITION_PRIVATE_DATA *Private;\r
+ PARTITION_ACCESS_TASK *Task;\r
+\r
+ Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This);\r
+\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Task = PartitionCreateAccessTask (Token);\r
+ if (Task == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = Private->DiskIo2->FlushDiskEx (Private->DiskIo2, &Task->DiskIo2Token);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->CloseEvent (Task->DiskIo2Token.Event);\r
+ FreePool (Task);\r
+ }\r
+ } else {\r
+ Status = Private->DiskIo2->FlushDiskEx (Private->DiskIo2, NULL);\r
+ }\r
+ return Status;\r
+}\r