/** @file Implementation of the SNP.Receive() function and its private helpers if any. Copyright (C) 2013, Red Hat, Inc. Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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; UINTN RxBufOffset; 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; MemoryFence (); 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; } RxBufOffset = (UINTN)(Dev->RxRing.Desc[DescIdx + 1].Addr - Dev->RxBufDeviceBase); RxPtr = Dev->RxBuf + RxBufOffset; CopyMem (Buffer, RxPtr, RxLen); if (DestAddr != NULL) { CopyMem (DestAddr, RxPtr, SIZE_OF_VNET (Mac)); } RxPtr += SIZE_OF_VNET (Mac); if (SrcAddr != NULL) { CopyMem (SrcAddr, RxPtr, SIZE_OF_VNET (Mac)); } RxPtr += SIZE_OF_VNET (Mac); 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] = (UINT16) DescIdx; MemoryFence (); *Dev->RxRing.Avail.Idx = AvailIdx; MemoryFence (); NotifyStatus = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX); if (!EFI_ERROR (Status)) { // earlier error takes precedence Status = NotifyStatus; } Exit: gBS->RestoreTPL (OldTpl); return Status; }