From b6dfc654c81f3d6b4720d6e3da182a8a2510d840 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 14 Jun 2013 07:40:24 +0000 Subject: OvmfPkg: VirtioNetDxe: implement Tx: SNP.Transmit and SNP.GetStatus Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Jordan Justen Reviewed-by: Stefan Hajnoczi git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14413 6f19259b-4bc3-4df7-8a09-765794883524 --- OvmfPkg/VirtioNetDxe/SnpGetStatus.c | 158 +++++++++++++++++++++++++++++++++ OvmfPkg/VirtioNetDxe/SnpTransmit.c | 169 ++++++++++++++++++++++++++++++++++++ 2 files changed, 327 insertions(+) create mode 100644 OvmfPkg/VirtioNetDxe/SnpGetStatus.c create mode 100644 OvmfPkg/VirtioNetDxe/SnpTransmit.c (limited to 'OvmfPkg/VirtioNetDxe') diff --git a/OvmfPkg/VirtioNetDxe/SnpGetStatus.c b/OvmfPkg/VirtioNetDxe/SnpGetStatus.c new file mode 100644 index 0000000000..ce6a7295fe --- /dev/null +++ b/OvmfPkg/VirtioNetDxe/SnpGetStatus.c @@ -0,0 +1,158 @@ +/** @file + + Implementation of the SNP.GetStatus() function and its private helpers if + any. + + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ + 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 "VirtioNet.h" + +/** + Reads the current interrupt status and recycled transmit buffer status from + a network interface. + + @param This The protocol instance pointer. + @param InterruptStatus A pointer to the bit mask of the currently active + interrupts If this is NULL, the interrupt status will + not be read from the device. If this is not NULL, the + interrupt status will be read from the device. When + the interrupt status is read, it will also be + cleared. Clearing the transmit interrupt does not + empty the recycled transmit buffer array. + @param TxBuf Recycled transmit buffer address. The network + interface will not transmit if its internal recycled + transmit buffer array is full. Reading the transmit + buffer does not clear the transmit interrupt. If this + is NULL, then the transmit buffer status will not be + read. If there are no transmit buffers to recycle and + TxBuf is not NULL, * TxBuf will be set to NULL. + + @retval EFI_SUCCESS The status of the network interface was + retrieved. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +VirtioNetGetStatus ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINT32 *InterruptStatus OPTIONAL, + OUT VOID **TxBuf OPTIONAL + ) +{ + VNET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT16 RxCurUsed; + UINT16 TxCurUsed; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = VIRTIO_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + // + // update link status + // + if (Dev->Snm.MediaPresentSupported) { + UINT16 LinkStatus; + + Status = VIRTIO_CFG_READ (Dev, VhdrLinkStatus, &LinkStatus); + if (EFI_ERROR (Status)) { + goto Exit; + } + Dev->Snm.MediaPresent = !!(LinkStatus & VIRTIO_NET_S_LINK_UP); + } + + // + // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device + // + MemoryFence (); + RxCurUsed = *Dev->RxRing.Used.Idx; + TxCurUsed = *Dev->TxRing.Used.Idx; + + if (InterruptStatus != NULL) { + // + // report the receive interrupt if there is data available for reception, + // report the transmit interrupt if we have transmitted at least one buffer + // + *InterruptStatus = 0; + if (Dev->RxLastUsed != RxCurUsed) { + *InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + } + if (Dev->TxLastUsed != TxCurUsed) { + ASSERT (Dev->TxCurPending > 0); + *InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + } + } + + if (TxBuf != NULL) { + if (Dev->TxLastUsed == TxCurUsed) { + *TxBuf = NULL; + } + else { + UINT16 UsedElemIdx; + UINT32 DescIdx; + + // + // fetch the first descriptor among those that the hypervisor reports + // completed + // + ASSERT (Dev->TxCurPending > 0); + ASSERT (Dev->TxCurPending <= Dev->TxMaxPending); + + UsedElemIdx = Dev->TxLastUsed++ % Dev->TxRing.QueueSize; + DescIdx = Dev->TxRing.Used.UsedElem[UsedElemIdx].Id; + ASSERT (DescIdx < 2 * Dev->TxMaxPending - 1); + + // + // report buffer address to caller that has been enqueued by caller + // + *TxBuf = (VOID *) Dev->TxRing.Desc[DescIdx + 1].Addr; + + // + // now this descriptor can be used again to enqueue a transmit buffer + // + Dev->TxFreeStack[--Dev->TxCurPending] = (UINT16) DescIdx; + } + } + + Status = EFI_SUCCESS; + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} diff --git a/OvmfPkg/VirtioNetDxe/SnpTransmit.c b/OvmfPkg/VirtioNetDxe/SnpTransmit.c new file mode 100644 index 0000000000..5bfb807edc --- /dev/null +++ b/OvmfPkg/VirtioNetDxe/SnpTransmit.c @@ -0,0 +1,169 @@ +/** @file + + Implementation of the SNP.Transmit() function and its private helpers if any. + + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+ + 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 "VirtioNet.h" + +/** + Places a packet in the transmit queue of a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then + it must be equal to This->Mode->MediaHeaderSize and the + DestAddr and Protocol parameters must not be NULL. + @param BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param Buffer A pointer to the packet (media header followed by data) to + be transmitted. This parameter cannot be NULL. If + HeaderSize is zero, then the media header in Buffer must + already be filled in by the caller. If HeaderSize is + non-zero, then the media header will be filled in by the + Transmit() function. + @param SrcAddr The source HW MAC address. If HeaderSize is zero, then + this parameter is ignored. If HeaderSize is non-zero and + SrcAddr is NULL, then This->Mode->CurrentAddress is used + for the source HW MAC address. + @param DestAddr The destination HW MAC address. If HeaderSize is zero, + then this parameter is ignored. + @param Protocol The type of header to build. If HeaderSize is zero, then + this parameter is ignored. See RFC 1700, section "Ether + Types", for examples. + + @retval EFI_SUCCESS The packet was placed on the transmit queue. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept + this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an + unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network + interface. + @retval EFI_UNSUPPORTED This function is not supported by the network + interface. + +**/ + +EFI_STATUS +EFIAPI +VirtioNetTransmit ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN /* +OUT! */ VOID *Buffer, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DestAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ) +{ + VNET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT16 DescIdx; + UINT16 AvailIdx; + + if (This == NULL || BufferSize == 0 || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev = VIRTIO_NET_FROM_SNP (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + switch (Dev->Snm.State) { + case EfiSimpleNetworkStopped: + Status = EFI_NOT_STARTED; + goto Exit; + case EfiSimpleNetworkStarted: + Status = EFI_DEVICE_ERROR; + goto Exit; + default: + break; + } + + if (BufferSize < Dev->Snm.MediaHeaderSize) { + Status = EFI_BUFFER_TOO_SMALL; + goto Exit; + } + if (BufferSize > Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // check if we have room for transmission + // + ASSERT (Dev->TxCurPending <= Dev->TxMaxPending); + if (Dev->TxCurPending == Dev->TxMaxPending) { + Status = EFI_NOT_READY; + goto Exit; + } + + // + // the caller may want us to fill in the media header: + // dst MAC, src MAC, Ethertype + // + if (HeaderSize != 0) { + UINT8 *Ptr; + + if (HeaderSize != Dev->Snm.MediaHeaderSize || + DestAddr == NULL || Protocol == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + Ptr = Buffer; + ASSERT (SIZE_OF_VNET (VhdrMac) <= sizeof (EFI_MAC_ADDRESS)); + + CopyMem (Ptr, DestAddr, SIZE_OF_VNET (VhdrMac)); + Ptr += SIZE_OF_VNET (VhdrMac); + + CopyMem (Ptr, + (SrcAddr == NULL) ? &Dev->Snm.CurrentAddress : SrcAddr, + SIZE_OF_VNET (VhdrMac)); + Ptr += SIZE_OF_VNET (VhdrMac); + + *Ptr++ = (UINT8) (*Protocol >> 8); + *Ptr++ = (UINT8) *Protocol; + + ASSERT (Ptr - (UINT8 *) Buffer == Dev->Snm.MediaHeaderSize); + } + + // + // virtio-0.9.5, 2.4.1 Supplying Buffers to The Device + // + DescIdx = Dev->TxFreeStack[Dev->TxCurPending++]; + Dev->TxRing.Desc[DescIdx + 1].Addr = (UINTN) Buffer; + Dev->TxRing.Desc[DescIdx + 1].Len = (UINT32) BufferSize; + + // + // the available index is never written by the host, we can read it back + // without a barrier + // + AvailIdx = *Dev->TxRing.Avail.Idx; + Dev->TxRing.Avail.Ring[AvailIdx++ % Dev->TxRing.QueueSize] = DescIdx; + + MemoryFence (); + *Dev->TxRing.Avail.Idx = AvailIdx; + + MemoryFence (); + Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, VIRTIO_NET_Q_TX); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} -- cgit v1.2.3