]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: VirtioNetDxe: implement Tx: SNP.Transmit and SNP.GetStatus
authorLaszlo Ersek <lersek@redhat.com>
Fri, 14 Jun 2013 07:40:24 +0000 (07:40 +0000)
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 14 Jun 2013 07:40:24 +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@14413 6f19259b-4bc3-4df7-8a09-765794883524

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

diff --git a/OvmfPkg/VirtioNetDxe/SnpGetStatus.c b/OvmfPkg/VirtioNetDxe/SnpGetStatus.c
new file mode 100644 (file)
index 0000000..ce6a729
--- /dev/null
@@ -0,0 +1,158 @@
+/** @file\r
+\r
+  Implementation of the SNP.GetStatus() function and its private helpers if\r
+  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/UefiBootServicesTableLib.h>\r
+\r
+#include "VirtioNet.h"\r
+\r
+/**\r
+  Reads the current interrupt status and recycled transmit buffer status from\r
+  a network interface.\r
+\r
+  @param  This            The protocol instance pointer.\r
+  @param  InterruptStatus A pointer to the bit mask of the currently active\r
+                          interrupts If this is NULL, the interrupt status will\r
+                          not be read from the device. If this is not NULL, the\r
+                          interrupt status will be read from the device. When\r
+                          the  interrupt status is read, it will also be\r
+                          cleared. Clearing the transmit  interrupt does not\r
+                          empty the recycled transmit buffer array.\r
+  @param  TxBuf           Recycled transmit buffer address. The network\r
+                          interface will not transmit if its internal recycled\r
+                          transmit buffer array is full. Reading the transmit\r
+                          buffer does not clear the transmit interrupt. If this\r
+                          is NULL, then the transmit buffer status will not be\r
+                          read. If there are no transmit buffers to recycle and\r
+                          TxBuf is not NULL, * TxBuf will be set to NULL.\r
+\r
+  @retval EFI_SUCCESS           The status of the network interface was\r
+                                retrieved.\r
+  @retval EFI_NOT_STARTED       The network interface has not been started.\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
+VirtioNetGetStatus (\r
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,\r
+  OUT UINT32                     *InterruptStatus OPTIONAL,\r
+  OUT VOID                       **TxBuf OPTIONAL\r
+  )\r
+{\r
+  VNET_DEV   *Dev;\r
+  EFI_TPL    OldTpl;\r
+  EFI_STATUS Status;\r
+  UINT16     RxCurUsed;\r
+  UINT16     TxCurUsed;\r
+\r
+  if (This == 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
+  // update link status\r
+  //\r
+  if (Dev->Snm.MediaPresentSupported) {\r
+    UINT16 LinkStatus;\r
+\r
+    Status = VIRTIO_CFG_READ (Dev, VhdrLinkStatus, &LinkStatus);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+    Dev->Snm.MediaPresent = !!(LinkStatus & VIRTIO_NET_S_LINK_UP);\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
+  TxCurUsed = *Dev->TxRing.Used.Idx;\r
+\r
+  if (InterruptStatus != NULL) {\r
+    //\r
+    // report the receive interrupt if there is data available for reception,\r
+    // report the transmit interrupt if we have transmitted at least one buffer\r
+    //\r
+    *InterruptStatus = 0;\r
+    if (Dev->RxLastUsed != RxCurUsed) {\r
+      *InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;\r
+    }\r
+    if (Dev->TxLastUsed != TxCurUsed) {\r
+      ASSERT (Dev->TxCurPending > 0);\r
+      *InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;\r
+    }\r
+  }\r
+\r
+  if (TxBuf != NULL) {\r
+    if (Dev->TxLastUsed == TxCurUsed) {\r
+      *TxBuf = NULL;\r
+    }\r
+    else {\r
+      UINT16 UsedElemIdx;\r
+      UINT32 DescIdx;\r
+\r
+      //\r
+      // fetch the first descriptor among those that the hypervisor reports\r
+      // completed\r
+      //\r
+      ASSERT (Dev->TxCurPending > 0);\r
+      ASSERT (Dev->TxCurPending <= Dev->TxMaxPending);\r
+\r
+      UsedElemIdx = Dev->TxLastUsed++ % Dev->TxRing.QueueSize;\r
+      DescIdx = Dev->TxRing.Used.UsedElem[UsedElemIdx].Id;\r
+      ASSERT (DescIdx < 2 * Dev->TxMaxPending - 1);\r
+\r
+      //\r
+      // report buffer address to caller that has been enqueued by caller\r
+      //\r
+      *TxBuf = (VOID *) Dev->TxRing.Desc[DescIdx + 1].Addr;\r
+\r
+      //\r
+      // now this descriptor can be used again to enqueue a transmit buffer\r
+      //\r
+      Dev->TxFreeStack[--Dev->TxCurPending] = (UINT16) DescIdx;\r
+    }\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r
diff --git a/OvmfPkg/VirtioNetDxe/SnpTransmit.c b/OvmfPkg/VirtioNetDxe/SnpTransmit.c
new file mode 100644 (file)
index 0000000..5bfb807
--- /dev/null
@@ -0,0 +1,169 @@
+/** @file\r
+\r
+  Implementation of the SNP.Transmit() 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
+  Places a packet in the transmit queue of a network interface.\r
+\r
+  @param  This       The protocol instance pointer.\r
+  @param  HeaderSize The size, in bytes, of the media header to be filled in by\r
+                     the Transmit() function. If HeaderSize is non-zero, then\r
+                     it must be equal to This->Mode->MediaHeaderSize and the\r
+                     DestAddr and Protocol parameters must not be NULL.\r
+  @param  BufferSize The size, in bytes, of the entire packet (media header and\r
+                     data) to be transmitted through the network interface.\r
+  @param  Buffer     A pointer to the packet (media header followed by data) to\r
+                     be transmitted. This parameter cannot be NULL. If\r
+                     HeaderSize is zero, then the media header in Buffer must\r
+                     already be filled in by the caller. If HeaderSize is\r
+                     non-zero, then the media header will be filled in by the\r
+                     Transmit() function.\r
+  @param  SrcAddr    The source HW MAC address. If HeaderSize is zero, then\r
+                     this parameter is ignored. If HeaderSize is non-zero and\r
+                     SrcAddr is NULL, then This->Mode->CurrentAddress is used\r
+                     for the source HW MAC address.\r
+  @param  DestAddr   The destination HW MAC address. If HeaderSize is zero,\r
+                     then this parameter is ignored.\r
+  @param  Protocol   The type of header to build. If HeaderSize is zero, then\r
+                     this parameter is ignored. See RFC 1700, section "Ether\r
+                     Types", for examples.\r
+\r
+  @retval EFI_SUCCESS           The packet was placed on the transmit queue.\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
+VirtioNetTransmit (\r
+  IN EFI_SIMPLE_NETWORK_PROTOCOL *This,\r
+  IN UINTN                       HeaderSize,\r
+  IN UINTN                       BufferSize,\r
+  IN /* +OUT! */ VOID            *Buffer,\r
+  IN EFI_MAC_ADDRESS             *SrcAddr  OPTIONAL,\r
+  IN EFI_MAC_ADDRESS             *DestAddr OPTIONAL,\r
+  IN UINT16                      *Protocol OPTIONAL\r
+  )\r
+{\r
+  VNET_DEV   *Dev;\r
+  EFI_TPL    OldTpl;\r
+  EFI_STATUS Status;\r
+  UINT16     DescIdx;\r
+  UINT16     AvailIdx;\r
+\r
+  if (This == NULL || BufferSize == 0 || 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
+  if (BufferSize < Dev->Snm.MediaHeaderSize) {\r
+    Status = EFI_BUFFER_TOO_SMALL;\r
+    goto Exit;\r
+  }\r
+  if (BufferSize > Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // check if we have room for transmission\r
+  //\r
+  ASSERT (Dev->TxCurPending <= Dev->TxMaxPending);\r
+  if (Dev->TxCurPending == Dev->TxMaxPending) {\r
+    Status = EFI_NOT_READY;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // the caller may want us to fill in the media header:\r
+  // dst MAC, src MAC, Ethertype\r
+  //\r
+  if (HeaderSize != 0) {\r
+    UINT8 *Ptr;\r
+\r
+    if (HeaderSize != Dev->Snm.MediaHeaderSize ||\r
+        DestAddr == NULL || Protocol == NULL) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Exit;\r
+    }\r
+    Ptr = Buffer;\r
+    ASSERT (SIZE_OF_VNET (VhdrMac) <= sizeof (EFI_MAC_ADDRESS));\r
+\r
+    CopyMem (Ptr, DestAddr, SIZE_OF_VNET (VhdrMac));\r
+    Ptr += SIZE_OF_VNET (VhdrMac);\r
+\r
+    CopyMem (Ptr,\r
+      (SrcAddr == NULL) ? &Dev->Snm.CurrentAddress : SrcAddr,\r
+      SIZE_OF_VNET (VhdrMac));\r
+    Ptr += SIZE_OF_VNET (VhdrMac);\r
+\r
+    *Ptr++ = (UINT8) (*Protocol >> 8);\r
+    *Ptr++ = (UINT8) *Protocol;\r
+\r
+    ASSERT (Ptr - (UINT8 *) Buffer == Dev->Snm.MediaHeaderSize);\r
+  }\r
+\r
+  //\r
+  // virtio-0.9.5, 2.4.1 Supplying Buffers to The Device\r
+  //\r
+  DescIdx = Dev->TxFreeStack[Dev->TxCurPending++];\r
+  Dev->TxRing.Desc[DescIdx + 1].Addr  = (UINTN) Buffer;\r
+  Dev->TxRing.Desc[DescIdx + 1].Len   = (UINT32) BufferSize;\r
+\r
+  //\r
+  // the available index is never written by the host, we can read it back\r
+  // without a barrier\r
+  //\r
+  AvailIdx = *Dev->TxRing.Avail.Idx;\r
+  Dev->TxRing.Avail.Ring[AvailIdx++ % Dev->TxRing.QueueSize] = DescIdx;\r
+\r
+  MemoryFence ();\r
+  *Dev->TxRing.Avail.Idx = AvailIdx;\r
+\r
+  MemoryFence ();\r
+  Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, VIRTIO_NET_Q_TX);\r
+\r
+Exit:\r
+  gBS->RestoreTPL (OldTpl);\r
+  return Status;\r
+}\r