From e12fadc3cf1735210f10711ebac2ba731048ae69 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 14 Jun 2013 07:40:17 +0000 Subject: OvmfPkg: VirtioNetDxe: SNP.Receive 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@14412 6f19259b-4bc3-4df7-8a09-765794883524 --- OvmfPkg/VirtioNetDxe/SnpReceive.c | 188 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 OvmfPkg/VirtioNetDxe/SnpReceive.c (limited to 'OvmfPkg') diff --git a/OvmfPkg/VirtioNetDxe/SnpReceive.c b/OvmfPkg/VirtioNetDxe/SnpReceive.c new file mode 100644 index 0000000000..eb3a2ba9b5 --- /dev/null +++ b/OvmfPkg/VirtioNetDxe/SnpReceive.c @@ -0,0 +1,188 @@ +/** @file + + Implementation of the SNP.Receive() 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" + +/** + Receives a packet from a network interface. + + @param This The protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header received on the + network interface. If this parameter is NULL, then the + media header size will not be returned. + @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the + size, in bytes, of the packet that was received on the + network interface. + @param Buffer A pointer to the data buffer to receive both the media + header and the data. + @param SrcAddr The source HW MAC address. If this parameter is NULL, the + HW MAC source address will not be extracted from the media + header. + @param DestAddr The destination HW MAC address. If this parameter is NULL, + the HW MAC destination address will not be extracted from + the media header. + @param Protocol The media header type. If this parameter is NULL, then the + protocol will not be extracted from the media header. See + RFC 1700 section "Ether Types" for examples. + + @retval EFI_SUCCESS The received data was stored in Buffer, and + BufferSize has been updated to the number of + bytes received. + @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 +VirtioNetReceive ( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINTN *HeaderSize OPTIONAL, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ) +{ + VNET_DEV *Dev; + EFI_TPL OldTpl; + EFI_STATUS Status; + UINT16 RxCurUsed; + UINT16 UsedElemIdx; + UINT32 DescIdx; + UINT32 RxLen; + UINTN OrigBufferSize; + UINT8 *RxPtr; + UINT16 AvailIdx; + EFI_STATUS NotifyStatus; + + if (This == NULL || BufferSize == NULL || 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; + } + + // + // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device + // + MemoryFence (); + RxCurUsed = *Dev->RxRing.Used.Idx; + + if (Dev->RxLastUsed == RxCurUsed) { + Status = EFI_NOT_READY; + goto Exit; + } + + UsedElemIdx = Dev->RxLastUsed % Dev->RxRing.QueueSize; + DescIdx = Dev->RxRing.Used.UsedElem[UsedElemIdx].Id; + RxLen = Dev->RxRing.Used.UsedElem[UsedElemIdx].Len; + + // + // the virtio-net request header must be complete; we skip it + // + ASSERT (RxLen >= Dev->RxRing.Desc[DescIdx].Len); + RxLen -= Dev->RxRing.Desc[DescIdx].Len; + // + // the host must not have filled in more data than requested + // + ASSERT (RxLen <= Dev->RxRing.Desc[DescIdx + 1].Len); + + OrigBufferSize = *BufferSize; + *BufferSize = RxLen; + + if (OrigBufferSize < RxLen) { + Status = EFI_BUFFER_TOO_SMALL; + goto Exit; // keep the packet + } + + if (RxLen < Dev->Snm.MediaHeaderSize) { + Status = EFI_DEVICE_ERROR; + goto RecycleDesc; // drop useless short packet + } + + if (HeaderSize != NULL) { + *HeaderSize = Dev->Snm.MediaHeaderSize; + } + + RxPtr = (UINT8 *)Dev->RxRing.Desc[DescIdx + 1].Addr; + CopyMem (Buffer, RxPtr, RxLen); + + if (DestAddr != NULL) { + CopyMem (DestAddr, RxPtr, SIZE_OF_VNET (VhdrMac)); + } + RxPtr += SIZE_OF_VNET (VhdrMac); + + if (SrcAddr != NULL) { + CopyMem (SrcAddr, RxPtr, SIZE_OF_VNET (VhdrMac)); + } + RxPtr += SIZE_OF_VNET (VhdrMac); + + if (Protocol != NULL) { + *Protocol = ((UINT16) RxPtr[0] << 8) | RxPtr[1]; + } + RxPtr += sizeof (UINT16); + + Status = EFI_SUCCESS; + +RecycleDesc: + ++Dev->RxLastUsed; + + // + // virtio-0.9.5, 2.4.1 Supplying Buffers to The Device + // + AvailIdx = *Dev->RxRing.Avail.Idx; + Dev->RxRing.Avail.Ring[AvailIdx++ % Dev->RxRing.QueueSize] = DescIdx; + + MemoryFence (); + *Dev->RxRing.Avail.Idx = AvailIdx; + + MemoryFence (); + NotifyStatus = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, + VIRTIO_NET_Q_RX); + + if (!EFI_ERROR (Status)) { // earlier error takes precedence + Status = NotifyStatus; + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} -- cgit v1.2.3