From 263559b87232826552c9e42c1652d9f9488c9870 Mon Sep 17 00:00:00 2001 From: jljusten Date: Fri, 12 Oct 2012 18:53:58 +0000 Subject: OvmfPkg: extract VirtioLib from VirtioBlkDxe Introduce a new library called VirtioLib, for now only collecting the following reusable functions with as little changes as possible: - VirtioWrite() - VirtioRead() - VirtioRingInit() - VirtioRingUninit() - AppendDesc() Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Jordan Justen git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13842 6f19259b-4bc3-4df7-8a09-765794883524 --- OvmfPkg/Library/VirtioLib/VirtioLib.c | 335 ++++++++++++++++++++++++++++++++ OvmfPkg/Library/VirtioLib/VirtioLib.inf | 34 ++++ 2 files changed, 369 insertions(+) create mode 100644 OvmfPkg/Library/VirtioLib/VirtioLib.c create mode 100644 OvmfPkg/Library/VirtioLib/VirtioLib.inf (limited to 'OvmfPkg/Library/VirtioLib') diff --git a/OvmfPkg/Library/VirtioLib/VirtioLib.c b/OvmfPkg/Library/VirtioLib/VirtioLib.c new file mode 100644 index 0000000000..580824261c --- /dev/null +++ b/OvmfPkg/Library/VirtioLib/VirtioLib.c @@ -0,0 +1,335 @@ +/** @file + + Utility functions used by virtio device drivers. + + Copyright (C) 2012, Red Hat, Inc. + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +#include + + +/** + + Write a word into Region 0 of the device specified by PciIo. + + Region 0 must be an iomem region. This is an internal function for the + driver-specific VIRTIO_CFG_WRITE() macros. + + @param[in] PciIo Target PCI device. + + @param[in] FieldOffset Destination offset. + + @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }. + + @param[in] Value Little endian value to write, converted to UINT64. + The least significant FieldSize bytes will be used. + + + @return Status code returned by PciIo->Io.Write(). + +**/ +EFIAPI +EFI_STATUS +VirtioWrite ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINT64 Value + ) +{ + UINTN Count; + EFI_PCI_IO_PROTOCOL_WIDTH Width; + + Count = 1; + switch (FieldSize) { + case 1: + Width = EfiPciIoWidthUint8; + break; + + case 2: + Width = EfiPciIoWidthUint16; + break; + + case 8: + Count = 2; + // fall through + + case 4: + Width = EfiPciIoWidthUint32; + break; + + default: + ASSERT (FALSE); + } + + return PciIo->Io.Write ( + PciIo, + Width, + PCI_BAR_IDX0, + FieldOffset, + Count, + &Value + ); +} + + +/** + + Read a word from Region 0 of the device specified by PciIo. + + Region 0 must be an iomem region. This is an internal function for the + driver-specific VIRTIO_CFG_READ() macros. + + @param[in] PciIo Source PCI device. + + @param[in] FieldOffset Source offset. + + @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }. + + @param[in] BufferSize Number of bytes available in the target buffer. Must + equal FieldSize. + + @param[out] Buffer Target buffer. + + + @return Status code returned by PciIo->Io.Read(). + +**/ +EFIAPI +EFI_STATUS +VirtioRead ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINTN FieldOffset, + IN UINTN FieldSize, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + UINTN Count; + EFI_PCI_IO_PROTOCOL_WIDTH Width; + + ASSERT (FieldSize == BufferSize); + + Count = 1; + switch (FieldSize) { + case 1: + Width = EfiPciIoWidthUint8; + break; + + case 2: + Width = EfiPciIoWidthUint16; + break; + + case 8: + Count = 2; + // fall through + + case 4: + Width = EfiPciIoWidthUint32; + break; + + default: + ASSERT (FALSE); + } + + return PciIo->Io.Read ( + PciIo, + Width, + PCI_BAR_IDX0, + FieldOffset, + Count, + Buffer + ); +} + + +/** + + Configure a virtio ring. + + This function sets up internal storage (the guest-host communication area) + and lays out several "navigation" (ie. no-ownership) pointers to parts of + that storage. + + Relevant sections from the virtio-0.9.5 spec: + - 1.1 Virtqueues, + - 2.3 Virtqueue Configuration. + + @param[in] The number of descriptors to allocate for the + virtio ring, as requested by the host. + + @param[out] Ring The virtio ring to set up. + + @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous + pages for the requested QueueSize. Fields of + Ring have indeterminate value. + + @retval EFI_SUCCESS Allocation and setup successful. Ring->Base + (and nothing else) is responsible for + deallocation. + +**/ +EFI_STATUS +EFIAPI +VirtioRingInit ( + IN UINT16 QueueSize, + OUT VRING *Ring + ) +{ + UINTN RingSize; + volatile UINT8 *RingPagesPtr; + + RingSize = ALIGN_VALUE ( + sizeof *Ring->Desc * QueueSize + + sizeof *Ring->Avail.Flags + + sizeof *Ring->Avail.Idx + + sizeof *Ring->Avail.Ring * QueueSize + + sizeof *Ring->Avail.UsedEvent, + EFI_PAGE_SIZE); + + RingSize += ALIGN_VALUE ( + sizeof *Ring->Used.Flags + + sizeof *Ring->Used.Idx + + sizeof *Ring->Used.UsedElem * QueueSize + + sizeof *Ring->Used.AvailEvent, + EFI_PAGE_SIZE); + + Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize); + Ring->Base = AllocatePages (Ring->NumPages); + if (Ring->Base == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SetMem (Ring->Base, RingSize, 0x00); + RingPagesPtr = Ring->Base; + + Ring->Desc = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Desc * QueueSize; + + Ring->Avail.Flags = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.Flags; + + Ring->Avail.Idx = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.Idx; + + Ring->Avail.Ring = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize; + + Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Avail.UsedEvent; + + RingPagesPtr = (volatile UINT8 *) Ring->Base + + ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base, + EFI_PAGE_SIZE); + + Ring->Used.Flags = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.Flags; + + Ring->Used.Idx = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.Idx; + + Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize; + + Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr; + RingPagesPtr += sizeof *Ring->Used.AvailEvent; + + Ring->QueueSize = QueueSize; + return EFI_SUCCESS; +} + + +/** + + Tear down the internal resources of a configured virtio ring. + + The caller is responsible to stop the host from using this ring before + invoking this function: the VSTAT_DRIVER_OK bit must be clear in + VhdrDeviceStatus. + + @param[out] Ring The virtio ring to clean up. + +**/ +VOID +EFIAPI +VirtioRingUninit ( + IN OUT VRING *Ring + ) +{ + FreePages (Ring->Base, Ring->NumPages); + SetMem (Ring, sizeof *Ring, 0x00); +} + + +/** + + Append a contiguous buffer for transmission / reception via the virtio ring. + + This function implements the following sections from virtio-0.9.5: + - 2.4.1.1 Placing Buffers into the Descriptor Table + - 2.4.1.2 Updating the Available Ring + + Free space is taken as granted, since the individual drivers support only + synchronous requests and host side status is processed in lock-step with + request submission. It is the calling driver's responsibility to verify the + ring size in advance. + + @param[in out] Ring The virtio ring to append the buffer to, as a + descriptor. + + @param [in] BufferPhysAddr (Guest pseudo-physical) start address of the + transmit / receive buffer. + + @param [in] BufferSize Number of bytes to transmit or receive. + + @param [in] Flags A bitmask of VRING_DESC_F_* flags. The caller + computes this mask dependent on further buffers + to append and transfer direction. + VRING_DESC_F_INDIRECT is unsupported. The + VRING_DESC.Next field is always set, but the + host only interprets it dependent on + VRING_DESC_F_NEXT. + + @param [in] HeadIdx The index identifying the head buffer (first + buffer appended) belonging to this same + request. + + @param [in out] NextAvailIdx On input, the index identifying the next + descriptor available to carry the buffer. On + output, incremented by one, modulo 2^16. + +**/ +VOID +EFIAPI +AppendDesc ( + IN OUT VRING *Ring, + IN UINTN BufferPhysAddr, + IN UINT32 BufferSize, + IN UINT16 Flags, + IN UINT16 HeadIdx, + IN OUT UINT16 *NextAvailIdx + ) +{ + volatile VRING_DESC *Desc; + + Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize]; + Desc->Addr = BufferPhysAddr; + Desc->Len = BufferSize; + Desc->Flags = Flags; + Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] = + HeadIdx % Ring->QueueSize; + Desc->Next = *NextAvailIdx % Ring->QueueSize; +} diff --git a/OvmfPkg/Library/VirtioLib/VirtioLib.inf b/OvmfPkg/Library/VirtioLib/VirtioLib.inf new file mode 100644 index 0000000000..cfd93a2c2c --- /dev/null +++ b/OvmfPkg/Library/VirtioLib/VirtioLib.inf @@ -0,0 +1,34 @@ +## @file +# Library of virtio utility functions. +# +# Copyright (C) 2012, Red Hat, Inc. +# +# This program and the accompanying materials are licensed and made available +# under the terms and conditions of the BSD License which accompanies this +# distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VirtioLib + FILE_GUID = 90CED1D9-18F2-47CC-BF24-41EC29406637 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = VirtioLib + +[Sources] + VirtioLib.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + MemoryAllocationLib -- cgit v1.2.3