Install the BlockIo protocol. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Anthony PERARD <anthony.per...@citrix.com>
--- Change in V2: - Remove blockIo2 headers. - Fix few comment. - file header, copyright - Rewrite few comment and error messages - No more callback - Improving block read/write, increase to the max size in one request (instead of only 8pages) - Fix lastblock when it's a cdrom - Do uninitialisation when fail to install fail - few comment - Licenses --- OvmfPkg/XenPvBlkDxe/BlockIo.c | 292 ++++++++++++++++++++++++++++++++++++ OvmfPkg/XenPvBlkDxe/BlockIo.h | 124 +++++++++++++++ OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.c | 64 ++++++++ OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.h | 1 + OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf | 2 + 5 files changed, 483 insertions(+) create mode 100644 OvmfPkg/XenPvBlkDxe/BlockIo.c create mode 100644 OvmfPkg/XenPvBlkDxe/BlockIo.h diff --git a/OvmfPkg/XenPvBlkDxe/BlockIo.c b/OvmfPkg/XenPvBlkDxe/BlockIo.c new file mode 100644 index 0000000..f08339b --- /dev/null +++ b/OvmfPkg/XenPvBlkDxe/BlockIo.c @@ -0,0 +1,292 @@ +/** @file + BlockIo implementation for Xen PV Block driver. + + This file is implementing the interface between the actual driver in + BlockFront.c to the BlockIo protocol. + + Copyright (C) 2014, Citrix Ltd. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +**/ + +#include "XenPvBlkDxe.h" + +#include "BlockFront.h" + +/// +/// Block I/O Media structure +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_BLOCK_IO_MEDIA gXenPvBlkDxeBlockIoMedia = { + 0, // MediaId + FALSE, // RemovableMedia + FALSE, // MediaPresent + FALSE, // LogicalPartition + TRUE, // ReadOnly + FALSE, // WriteCaching + 512, // BlockSize + 512, // IoAlign, BlockFront does not support less than 512 bits-aligned. + 0, // LastBlock + 0, // LowestAlignedLba + 0, // LogicalBlocksPerPhysicalBlock + 0 // OptimalTransferLengthGranularity +}; + +/// +/// Block I/O Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_BLOCK_IO_PROTOCOL gXenPvBlkDxeBlockIo = { + EFI_BLOCK_IO_PROTOCOL_REVISION3, // Revision + &gXenPvBlkDxeBlockIoMedia, // Media + XenPvBlkDxeBlockIoReset, // Reset + XenPvBlkDxeBlockIoReadBlocks, // ReadBlocks + XenPvBlkDxeBlockIoWriteBlocks, // WriteBlocks + XenPvBlkDxeBlockIoFlushBlocks // FlushBlocks +}; + + + + +/** + Read/Write BufferSize bytes from Lba into Buffer. + + This function is commun to XenPvBlkDxeBlockIoReadBlocks and + XenPvBlkDxeBlockIoWriteBlocks. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from/write to. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination/source buffer for the data. + @param IsWrite Indicate if the operation is write or read. + + @return See description of XenPvBlkDxeBlockIoReadBlocks and + XenPvBlkDxeBlockIoWriteBlocks. +**/ +STATIC +EFI_STATUS +XenPvBlkDxeBlockIoReadWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN OUT VOID *Buffer, + IN BOOLEAN IsWrite + ) +{ + XEN_BLOCK_FRONT_IO IoData; + EFI_BLOCK_IO_MEDIA *Media; + UINTN NextOffset; + EFI_STATUS Status; + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + Media = This->Media; + + if (BufferSize % Media->BlockSize != 0) { + DEBUG ((EFI_D_ERROR, "XenPvBlkDxe: Bad buffer size: 0x%X\n", BufferSize)); + return EFI_BAD_BUFFER_SIZE; + } + + if (Lba > Media->LastBlock || + (BufferSize / Media->BlockSize) - 1 > Media->LastBlock - Lba) { + DEBUG ((EFI_D_ERROR, "XenPvBlkDxe: %a with invalid LBA: 0x%LX, size: 0x%x\n", + IsWrite ? "Write" : "Read", Lba, BufferSize)); + return EFI_INVALID_PARAMETER; + } + + if (IsWrite && Media->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + if ((Media->IoAlign > 1) && (UINTN)Buffer & (Media->IoAlign - 1)) { + // + // Grub2 does not appear to respect IoAlign of 512, so reallocate the + // buffer here. + // + VOID *NewBuffer; + + // + // Try again with a properly aligned buffer. + // + NewBuffer = AllocateAlignedPages((BufferSize + EFI_PAGE_SIZE) / EFI_PAGE_SIZE, + Media->IoAlign); + if (!IsWrite) { + Status = XenPvBlkDxeBlockIoReadBlocks (This, MediaId, + Lba, BufferSize, NewBuffer); + CopyMem (Buffer, NewBuffer, BufferSize); + } else { + CopyMem (NewBuffer, Buffer, BufferSize); + Status = XenPvBlkDxeBlockIoWriteBlocks (This, MediaId, + Lba, BufferSize, NewBuffer); + } + FreeAlignedPages (NewBuffer, (BufferSize + EFI_PAGE_SIZE) / EFI_PAGE_SIZE); + return Status; + } + + IoData.Dev = XEN_BLOCK_FRONT_FROM_BLOCK_IO (This); + NextOffset = Lba * Media->BlockSize; + + while (BufferSize > 0) { + if (((UINTN)Buffer & EFI_PAGE_MASK) == 0) { + IoData.Size = MIN (BLKIF_MAX_SEGMENTS_PER_REQUEST * EFI_PAGE_SIZE, + BufferSize); + } else { + IoData.Size = MIN ((BLKIF_MAX_SEGMENTS_PER_REQUEST - 1) * EFI_PAGE_SIZE, + BufferSize); + } + + IoData.Buffer = Buffer; + IoData.Offset = NextOffset; + BufferSize -= IoData.Size; + Buffer = (VOID*) ((UINTN) Buffer + IoData.Size); + NextOffset += IoData.Size; + Status = XenPvBlockIo (&IoData, IsWrite); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XenPvBlkDxe: Error durring %a operation.\n", + IsWrite ? "write" : "read")); + return Status; + } + } + return EFI_SUCCESS; +} + + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + return XenPvBlkDxeBlockIoReadWriteBlocks (This, + MediaId, Lba, BufferSize, Buffer, FALSE); +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return XenPvBlkDxeBlockIoReadWriteBlocks (This, + MediaId, Lba, BufferSize, Buffer, TRUE); +} + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writting back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + XenPvBlockSync (XEN_BLOCK_FRONT_FROM_BLOCK_IO (This)); + return EFI_SUCCESS; +} + +/** + Reset the block device hardware. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Not used. + + @retval EFI_SUCCESS The device was reset. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + // + // Since the initialization of the devices is done, then the device is + // working correctly. + // + return EFI_SUCCESS; +} diff --git a/OvmfPkg/XenPvBlkDxe/BlockIo.h b/OvmfPkg/XenPvBlkDxe/BlockIo.h new file mode 100644 index 0000000..22b6de2 --- /dev/null +++ b/OvmfPkg/XenPvBlkDxe/BlockIo.h @@ -0,0 +1,124 @@ +/** @file + BlockIo function declaration for Xen PV block driver. + + Copyright (C) 2014, Citrix Ltd. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +**/ + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writting back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +/** + Reset the block device hardware. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Not used. + + @retval EFI_SUCCESS The device was reset. + +**/ +EFI_STATUS +EFIAPI +XenPvBlkDxeBlockIoReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +extern EFI_BLOCK_IO_MEDIA gXenPvBlkDxeBlockIoMedia; +extern EFI_BLOCK_IO_PROTOCOL gXenPvBlkDxeBlockIo; diff --git a/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.c b/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.c index 930333f..d530600 100644 --- a/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.c +++ b/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.c @@ -281,6 +281,7 @@ XenPvBlkDxeDriverBindingStart ( EFI_STATUS Status; XENBUS_PROTOCOL *XenBusIo; XEN_BLOCK_FRONT_DEVICE *Dev; + EFI_BLOCK_IO_MEDIA *Media; Status = gBS->OpenProtocol ( ControllerHandle, @@ -299,8 +300,44 @@ XenPvBlkDxeDriverBindingStart ( goto CloseProtocol; } + CopyMem (&Dev->BlockIo, &gXenPvBlkDxeBlockIo, sizeof (EFI_BLOCK_IO_PROTOCOL)); + Media = AllocateCopyPool (sizeof (EFI_BLOCK_IO_MEDIA), + &gXenPvBlkDxeBlockIoMedia); + if (Dev->MediaInfo.VDiskInfo & VDISK_REMOVABLE) { + Media->RemovableMedia = TRUE; + } + Media->MediaPresent = TRUE; + Media->ReadOnly = !Dev->MediaInfo.ReadWrite; + if (Dev->MediaInfo.CdRom) { + // + // If it's a cdrom, the blocksize value need to be 2048 for OVMF to + // recognize it as a cdrom: + // MdeModulePkg/Universal/Disk/PartitionDxe/ElTorito.c + // + Media->BlockSize = 2048; + Media->LastBlock = DivU64x32 (Dev->MediaInfo.Sectors, + Media->BlockSize / Dev->MediaInfo.SectorSize) - 1; + } else { + Media->BlockSize = Dev->MediaInfo.SectorSize; + Media->LastBlock = Dev->MediaInfo.Sectors - 1; + } + Dev->BlockIo.Media = Media; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiBlockIoProtocolGuid, &Dev->BlockIo, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "XenPvBlk: install protocol fail: %r\n", Status)); + goto UninitBlockFront; + } + return EFI_SUCCESS; +UninitBlockFront: + FreePool (Media); + XenPvBlockFrontShutdown (Dev); CloseProtocol: gBS->CloseProtocol (ControllerHandle, &gXenBusProtocolGuid, This->DriverBindingHandle, ControllerHandle); @@ -342,6 +379,33 @@ XenPvBlkDxeDriverBindingStop ( IN EFI_HANDLE *ChildHandleBuffer OPTIONAL ) { + EFI_BLOCK_IO_PROTOCOL *BlockIo; + XEN_BLOCK_FRONT_DEVICE *Dev; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + ControllerHandle, &gEfiBlockIoProtocolGuid, + (VOID **)&BlockIo, + This->DriverBindingHandle, ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->UninstallProtocolInterface (ControllerHandle, + &gEfiBlockIoProtocolGuid, BlockIo); + if (EFI_ERROR (Status)) { + return Status; + } + + Media = BlockIo->Media; + Dev = XEN_BLOCK_FRONT_FROM_BLOCK_IO (BlockIo); + XenPvBlockFrontShutdown (Dev); + + FreePool (Media); + gBS->CloseProtocol (ControllerHandle, &gXenBusProtocolGuid, This->DriverBindingHandle, ControllerHandle); diff --git a/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.h b/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.h index 14c65b2..420e751 100644 --- a/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.h +++ b/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.h @@ -93,6 +93,7 @@ extern EFI_COMPONENT_NAME_PROTOCOL gXenPvBlkDxeComponentName; // #include "DriverBinding.h" #include "ComponentName.h" +#include "BlockIo.h" #endif diff --git a/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf b/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf index 619ed8f..b7c4a1f 100644 --- a/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf +++ b/OvmfPkg/XenPvBlkDxe/XenPvBlkDxe.inf @@ -52,6 +52,8 @@ ComponentName.h BlockFront.c BlockFront.h + BlockIo.c + BlockIo.h [LibraryClasses] -- Anthony PERARD ------------------------------------------------------------------------------ Slashdot TV. Video for Nerds. Stuff that matters. http://tv.slashdot.org/ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel