]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: VirtioNetDxe: SNP.Receive
authorLaszlo Ersek <lersek@redhat.com>
Fri, 14 Jun 2013 07:40:17 +0000 (07:40 +0000)
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 14 Jun 2013 07:40:17 +0000 (07:40 +0000)
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14412 6f19259b-4bc3-4df7-8a09-765794883524

OvmfPkg/VirtioNetDxe/SnpReceive.c [new file with mode: 0644]

diff --git a/OvmfPkg/VirtioNetDxe/SnpReceive.c b/OvmfPkg/VirtioNetDxe/SnpReceive.c
new file mode 100644 (file)
index 0000000..eb3a2ba
--- /dev/null
@@ -0,0 +1,188 @@
+/** @file\r
+\r
+  Implementation of the SNP.Receive() function and its private helpers if any.\r
+\r
+  Copyright (C) 2013, Red Hat, Inc.\r
+  Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials are licensed and made available\r
+  under the terms and conditions of the BSD License which accompanies this\r
+  distribution. The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#include "VirtioNet.h"\r
+\r
+/**\r
+  Receives a packet from a network interface.\r
+\r
+  @param  This       The protocol instance pointer.\r
+  @param  HeaderSize The size, in bytes, of the media header received on the\r
+                     network interface. If this parameter is NULL, then the\r
+                     media header size will not be returned.\r
+  @param  BufferSize On entry, the size, in bytes, of Buffer. On exit, the\r
+                     size, in bytes, of the packet that was received on the\r
+                     network interface.\r
+  @param  Buffer     A pointer to the data buffer to receive both the media\r
+                     header and the data.\r
+  @param  SrcAddr    The source HW MAC address. If this parameter is NULL, the\r
+                     HW MAC source address will not be extracted from the media\r
+                     header.\r
+  @param  DestAddr   The destination HW MAC address. If this parameter is NULL,\r
+                     the HW MAC destination address will not be extracted from\r
+                     the media header.\r
+  @param  Protocol   The media header type. If this parameter is NULL, then the\r
+                     protocol will not be extracted from the media header. See\r
+                     RFC 1700 section "Ether Types" for examples.\r
+\r
+  @retval  EFI_SUCCESS           The received data was stored in Buffer, and\r
+                                 BufferSize has been updated to the number of\r
+                                 bytes received.\r
+  @retval  EFI_NOT_STARTED       The network interface has not been started.\r
+  @retval  EFI_NOT_READY         The network interface is too busy to accept\r
+                                 this transmit request.\r
+  @retval  EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.\r
+  @retval  EFI_INVALID_PARAMETER One or more of the parameters has an\r
+                                 unsupported value.\r
+  @retval  EFI_DEVICE_ERROR      The command could not be sent to the network\r
+                                 interface.\r
+  @retval  EFI_UNSUPPORTED       This function is not supported by the network\r
+                                 interface.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioNetReceive (\r
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,\r
+  OUT UINTN                      *HeaderSize OPTIONAL,\r
+  IN OUT UINTN                   *BufferSize,\r
+  OUT VOID                       *Buffer,\r
+  OUT EFI_MAC_ADDRESS            *SrcAddr    OPTIONAL,\r
+  OUT EFI_MAC_ADDRESS            *DestAddr   OPTIONAL,\r
+  OUT UINT16                     *Protocol   OPTIONAL\r
+  )\r
+{\r
+  VNET_DEV   *Dev;\r
+  EFI_TPL    OldTpl;\r
+  EFI_STATUS Status;\r
+  UINT16     RxCurUsed;\r
+  UINT16     UsedElemIdx;\r
+  UINT32     DescIdx;\r
+  UINT32     RxLen;\r
+  UINTN      OrigBufferSize;\r
+  UINT8      *RxPtr;\r
+  UINT16     AvailIdx;\r
+  EFI_STATUS NotifyStatus;\r
+\r
+  if (This == NULL || BufferSize == NULL || Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Dev = VIRTIO_NET_FROM_SNP (This);\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  switch (Dev->Snm.State) {\r
+  case EfiSimpleNetworkStopped:\r
+    Status = EFI_NOT_STARTED;\r
+    goto Exit;\r
+  case EfiSimpleNetworkStarted:\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
+  default:\r
+    break;\r
+  }\r
+\r
+  //\r
+  // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device\r
+  //\r
+  MemoryFence ();\r
+  RxCurUsed = *Dev->RxRing.Used.Idx;\r
+\r
+  if (Dev->RxLastUsed == RxCurUsed) {\r
+    Status = EFI_NOT_READY;\r
+    goto Exit;\r
+  }\r
+\r
+  UsedElemIdx = Dev->RxLastUsed % Dev->RxRing.QueueSize;\r
+  DescIdx = Dev->RxRing.Used.UsedElem[UsedElemIdx].Id;\r
+  RxLen   = Dev->RxRing.Used.UsedElem[UsedElemIdx].Len;\r
+\r
+  //\r
+  // the virtio-net request header must be complete; we skip it\r
+  //\r
+  ASSERT (RxLen >= Dev->RxRing.Desc[DescIdx].Len);\r
+  RxLen -= Dev->RxRing.Desc[DescIdx].Len;\r
+  //\r
+  // the host must not have filled in more data than requested\r
+  //\r
+  ASSERT (RxLen <= Dev->RxRing.Desc[DescIdx + 1].Len);\r
+\r
+  OrigBufferSize = *BufferSize;\r
+  *BufferSize = RxLen;\r
+\r
+  if (OrigBufferSize < RxLen) {\r
+    Status = EFI_BUFFER_TOO_SMALL;\r
+    goto Exit; // keep the packet\r
+  }\r
+\r
+  if (RxLen < Dev->Snm.MediaHeaderSize) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto RecycleDesc; // drop useless short packet\r
+  }\r
+\r
+  if (HeaderSize != NULL) {\r
+    *HeaderSize = Dev->Snm.MediaHeaderSize;\r
+  }\r
+\r
+  RxPtr = (UINT8 *)Dev->RxRing.Desc[DescIdx + 1].Addr;\r
+  CopyMem (Buffer, RxPtr, RxLen);\r
+\r
+  if (DestAddr != NULL) {\r
+    CopyMem (DestAddr, RxPtr, SIZE_OF_VNET (VhdrMac));\r
+  }\r
+  RxPtr += SIZE_OF_VNET (VhdrMac);\r
+\r
+  if (SrcAddr != NULL) {\r
+    CopyMem (SrcAddr, RxPtr, SIZE_OF_VNET (VhdrMac));\r
+  }\r
+  RxPtr += SIZE_OF_VNET (VhdrMac);\r
+\r
+  if (Protocol != NULL) {\r
+    *Protocol = ((UINT16) RxPtr[0] << 8) | RxPtr[1];\r
+  }\r
+  RxPtr += sizeof (UINT16);\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+RecycleDesc:\r
+  ++Dev->RxLastUsed;\r
+\r
+  //\r
+  // virtio-0.9.5, 2.4.1 Supplying Buffers to The Device\r
+  //\r
+  AvailIdx = *Dev->RxRing.Avail.Idx;\r
+  Dev->RxRing.Avail.Ring[AvailIdx++ % Dev->RxRing.QueueSize] = DescIdx;\r
+\r
+  MemoryFence ();\r
+  *Dev->RxRing.Avail.Idx = AvailIdx;\r
+\r
+  MemoryFence ();\r
+  NotifyStatus = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify,\r
+                   VIRTIO_NET_Q_RX);\r
+\r
+  if (!EFI_ERROR (Status)) { // earlier error takes precedence\r
+    Status = NotifyStatus;\r
+  }\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r