virtio: add modern device support

Type: feature

Change-Id: I205f7c146a213d603d9d1e46fcf5195a876608dc
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
diff --git a/src/vnet/devices/virtio/virtio_pci_modern.c b/src/vnet/devices/virtio/virtio_pci_modern.c
new file mode 100644
index 0000000..24e8a4f
--- /dev/null
+++ b/src/vnet/devices/virtio/virtio_pci_modern.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <vppinfra/types.h>
+#include <vlib/vlib.h>
+#include <vlib/pci/pci.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/devices/virtio/virtio.h>
+#include <vnet/devices/virtio/virtio_pci_modern.h>
+#include <vnet/devices/virtio/pci.h>
+
+
+static u64
+virtio_pci_modern_get_device_features (vlib_main_t * vm, virtio_if_t * vif)
+{
+  u64 features_lo, features_hi;
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DEVICE_FEATURE_SELECT_OFFSET (vif),
+			    VIRTIO_FEATURE_SELECT_LO);
+  features_lo =
+    virtio_pci_reg_read_u32 (vif, VIRTIO_DEVICE_FEATURE_OFFSET (vif));
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DEVICE_FEATURE_SELECT_OFFSET (vif),
+			    VIRTIO_FEATURE_SELECT_HI);
+  features_hi =
+    virtio_pci_reg_read_u32 (vif, VIRTIO_DEVICE_FEATURE_OFFSET (vif));
+  u64 features = ((features_hi << 32) | features_lo);
+  return features;
+}
+
+static u64
+virtio_pci_modern_get_driver_features (vlib_main_t * vm, virtio_if_t * vif)
+{
+  u64 features_lo, features_hi;
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DRIVER_FEATURE_SELECT_OFFSET (vif),
+			    VIRTIO_FEATURE_SELECT_LO);
+  features_lo =
+    virtio_pci_reg_read_u32 (vif, VIRTIO_DRIVER_FEATURE_OFFSET (vif));
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DRIVER_FEATURE_SELECT_OFFSET (vif),
+			    VIRTIO_FEATURE_SELECT_HI);
+  features_hi =
+    virtio_pci_reg_read_u32 (vif, VIRTIO_DRIVER_FEATURE_OFFSET (vif));
+
+  vif->features = ((features_hi << 32) | features_lo);
+  return vif->features;
+}
+
+static void
+virtio_pci_modern_set_driver_features (vlib_main_t * vm, virtio_if_t * vif,
+				       u64 features)
+{
+  u32 features_lo = (u32) features, features_hi = (u32) (features >> 32);
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DRIVER_FEATURE_SELECT_OFFSET (vif),
+			    VIRTIO_FEATURE_SELECT_LO);
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DRIVER_FEATURE_OFFSET (vif),
+			    features_lo);
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DRIVER_FEATURE_SELECT_OFFSET (vif),
+			    VIRTIO_FEATURE_SELECT_HI);
+  virtio_pci_reg_write_u32 (vif, VIRTIO_DRIVER_FEATURE_OFFSET (vif),
+			    features_hi);
+
+  if (features != virtio_pci_modern_get_driver_features (vm, vif))
+    {
+      clib_warning ("modern set guest features failed!");
+    }
+}
+
+static u16
+virtio_pci_modern_get_msix_config (virtio_if_t * vif)
+{
+  u16 msix_config;
+  msix_config =
+    virtio_pci_reg_read_u16 (vif, VIRTIO_MSIX_CONFIG_VECTOR_OFFSET (vif));
+  return msix_config;
+}
+
+static u16
+virtio_pci_modern_set_msix_config (vlib_main_t * vm, virtio_if_t * vif,
+				   u16 msix_config)
+{
+  virtio_pci_reg_write_u16 (vif, VIRTIO_MSIX_CONFIG_VECTOR_OFFSET (vif),
+			    msix_config);
+  return virtio_pci_modern_get_msix_config (vif);
+}
+
+static u16
+virtio_pci_modern_get_num_queues (virtio_if_t * vif)
+{
+  u16 num_queues = 0;
+  num_queues = virtio_pci_reg_read_u16 (vif, VIRTIO_NUM_QUEUES_OFFSET (vif));
+  return num_queues;
+}
+
+static u8
+virtio_pci_modern_get_status (vlib_main_t * vm, virtio_if_t * vif)
+{
+  u8 status = 0;
+  status = virtio_pci_reg_read_u8 (vif, VIRTIO_DEVICE_STATUS_OFFSET (vif));
+  return status;
+}
+
+static void
+virtio_pci_modern_set_status (vlib_main_t * vm, virtio_if_t * vif, u8 status)
+{
+  if (status != VIRTIO_CONFIG_STATUS_RESET)
+    status |= virtio_pci_modern_get_status (vm, vif);
+  virtio_pci_reg_write_u8 (vif, VIRTIO_DEVICE_STATUS_OFFSET (vif), status);
+}
+
+static u8
+virtio_pci_modern_reset (vlib_main_t * vm, virtio_if_t * vif)
+{
+  virtio_pci_modern_set_status (vm, vif, VIRTIO_CONFIG_STATUS_RESET);
+  return virtio_pci_modern_get_status (vm, vif);
+}
+
+static u8
+virtio_pci_modern_get_config_generation (virtio_if_t * vif)
+{
+  u8 config_generation = 0;
+  config_generation =
+    virtio_pci_reg_read_u8 (vif, VIRTIO_CONFIG_GENERATION_OFFSET (vif));
+  return config_generation;
+}
+
+static void
+virtio_pci_modern_set_queue_select (virtio_if_t * vif, u16 queue_select)
+{
+  virtio_pci_reg_write_u16 (vif, VIRTIO_QUEUE_SELECT_OFFSET (vif),
+			    queue_select);
+}
+
+static u16
+virtio_pci_modern_get_queue_size (vlib_main_t * vm, virtio_if_t * vif,
+				  u16 queue_id)
+{
+  u16 queue_size = 0;
+  virtio_pci_modern_set_queue_select (vif, queue_id);
+  queue_size = virtio_pci_reg_read_u16 (vif, VIRTIO_QUEUE_SIZE_OFFSET (vif));
+  return queue_size;
+}
+
+static void
+virtio_pci_modern_set_queue_size (vlib_main_t * vm, virtio_if_t * vif,
+				  u16 queue_id, u16 queue_size)
+{
+  if (!is_pow2 (queue_size))
+    {
+      return;
+    }
+
+  if (virtio_pci_modern_get_queue_size (vm, vif, queue_id) > queue_size)
+    virtio_pci_reg_write_u16 (vif, VIRTIO_QUEUE_SIZE_OFFSET (vif),
+			      queue_size);
+}
+
+static u16
+virtio_pci_modern_get_queue_msix_vector (virtio_if_t * vif)
+{
+  u16 queue_msix_vector = 0;
+  queue_msix_vector =
+    virtio_pci_reg_read_u16 (vif, VIRTIO_QUEUE_MSIX_VECTOR_OFFSET (vif));
+  return queue_msix_vector;
+}
+
+static u16
+virtio_pci_modern_set_queue_msix_vector (vlib_main_t * vm, virtio_if_t * vif,
+					 u16 queue_msix_vector, u16 queue_id)
+{
+  virtio_pci_modern_set_queue_select (vif, queue_id);
+  virtio_pci_reg_write_u16 (vif, VIRTIO_QUEUE_MSIX_VECTOR_OFFSET (vif),
+			    queue_msix_vector);
+  return virtio_pci_modern_get_queue_msix_vector (vif);
+}
+
+static u16
+virtio_pci_modern_get_queue_enable (virtio_if_t * vif, u16 queue_id)
+{
+  u16 queue_enable = 0;
+  virtio_pci_modern_set_queue_select (vif, queue_id);
+  queue_enable =
+    virtio_pci_reg_read_u16 (vif, VIRTIO_QUEUE_ENABLE_OFFSET (vif));
+  return queue_enable;
+}
+
+static void
+virtio_pci_modern_set_queue_enable (virtio_if_t * vif, u16 queue_id,
+				    u16 queue_enable)
+{
+  virtio_pci_modern_set_queue_select (vif, queue_id);
+  virtio_pci_reg_write_u16 (vif, VIRTIO_QUEUE_ENABLE_OFFSET (vif),
+			    queue_enable);
+}
+
+static u16
+virtio_pci_modern_get_queue_notify_off (virtio_if_t * vif, u16 queue_id)
+{
+  u16 queue_notify_off = 0;
+  virtio_pci_modern_set_queue_select (vif, queue_id);
+  queue_notify_off =
+    virtio_pci_reg_read_u16 (vif, VIRTIO_QUEUE_NOTIFY_OFF_OFFSET (vif));
+  return queue_notify_off;
+}
+
+static u64
+virtio_pci_modern_get_queue_desc (virtio_if_t * vif)
+{
+  u64 queue_desc = 0;
+  queue_desc = virtio_pci_reg_read_u64 (vif, VIRTIO_QUEUE_DESC_OFFSET (vif));
+  return queue_desc;
+}
+
+static void
+virtio_pci_modern_set_queue_desc (virtio_if_t * vif, u64 queue_desc)
+{
+  virtio_pci_reg_write_u64 (vif, VIRTIO_QUEUE_DESC_OFFSET (vif), queue_desc);
+}
+
+static u64
+virtio_pci_modern_get_queue_driver (virtio_if_t * vif)
+{
+  u64 queue_driver = 0;
+  queue_driver =
+    virtio_pci_reg_read_u64 (vif, VIRTIO_QUEUE_DRIVER_OFFSET (vif));
+  return queue_driver;
+}
+
+static void
+virtio_pci_modern_set_queue_driver (virtio_if_t * vif, u64 queue_driver)
+{
+  virtio_pci_reg_write_u64 (vif, VIRTIO_QUEUE_DRIVER_OFFSET (vif),
+			    queue_driver);
+}
+
+static u64
+virtio_pci_modern_get_queue_device (virtio_if_t * vif)
+{
+  u64 queue_device = 0;
+  queue_device =
+    virtio_pci_reg_read_u64 (vif, VIRTIO_QUEUE_DEVICE_OFFSET (vif));
+  return queue_device;
+}
+
+static void
+virtio_pci_modern_set_queue_device (virtio_if_t * vif, u64 queue_device)
+{
+  virtio_pci_reg_write_u64 (vif, VIRTIO_QUEUE_DEVICE_OFFSET (vif),
+			    queue_device);
+}
+
+static u8
+virtio_pci_modern_setup_queue (vlib_main_t * vm, virtio_if_t * vif,
+			       u16 queue_id, void *p)
+{
+  struct vring vr;
+  u16 queue_size = 0;
+
+  virtio_pci_modern_set_queue_select (vif, queue_id);
+  queue_size = virtio_pci_modern_get_queue_size (vm, vif, queue_id);
+  vring_init (&vr, queue_size, p, VIRTIO_PCI_VRING_ALIGN);
+
+  u64 desc = vlib_physmem_get_pa (vm, vr.desc);
+  virtio_pci_modern_set_queue_desc (vif, desc);
+  if (desc != virtio_pci_modern_get_queue_desc (vif))
+    return 1;
+
+  u64 avail = vlib_physmem_get_pa (vm, vr.avail);
+  virtio_pci_modern_set_queue_driver (vif, avail);
+  if (avail != virtio_pci_modern_get_queue_driver (vif))
+    return 1;
+
+  u64 used = vlib_physmem_get_pa (vm, vr.used);
+  virtio_pci_modern_set_queue_device (vif, used);
+  if (used != virtio_pci_modern_get_queue_device (vif))
+    return 1;
+
+  virtio_pci_modern_set_queue_enable (vif, queue_id, 1);
+
+  if (virtio_pci_modern_get_queue_enable (vif, queue_id))
+    return 0;
+
+  return 1;
+}
+
+static void
+virtio_pci_modern_del_queue (vlib_main_t * vm, virtio_if_t * vif,
+			     u16 queue_id)
+{
+  virtio_pci_modern_set_queue_select (vif, queue_id);
+  virtio_pci_modern_set_queue_enable (vif, queue_id, 0);
+  virtio_pci_modern_set_queue_desc (vif, 0);
+  virtio_pci_modern_set_queue_driver (vif, 0);
+  virtio_pci_modern_set_queue_device (vif, 0);
+}
+
+static void
+virtio_pci_modern_get_device_mac (vlib_main_t * vm, virtio_if_t * vif)
+{
+  *((u32 *) vif->mac_addr) =
+    virtio_pci_reg_read_u32 (vif, VIRTIO_MAC_OFFSET (vif));
+  *((u16 *) (vif->mac_addr + 4)) =
+    virtio_pci_reg_read_u16 (vif, VIRTIO_MAC_OFFSET (vif) + 4);
+}
+
+static void
+virtio_pci_modern_set_device_mac (vlib_main_t * vm, virtio_if_t * vif)
+{
+  virtio_pci_reg_write_u32 (vif, VIRTIO_MAC_OFFSET (vif),
+			    *((u32 *) vif->mac_addr));
+  virtio_pci_reg_write_u16 (vif, VIRTIO_MAC_OFFSET (vif) + 4,
+			    *((u16 *) (vif->mac_addr + 4)));
+}
+
+static u16
+virtio_pci_modern_get_device_status (vlib_main_t * vm, virtio_if_t * vif)
+{
+  u16 status = 0;
+  status = virtio_pci_reg_read_u16 (vif, VIRTIO_STATUS_OFFSET (vif));
+  return status;
+}
+
+static u16
+virtio_pci_modern_get_max_virtqueue_pairs (vlib_main_t * vm,
+					   virtio_if_t * vif)
+{
+  u16 max_virtqueue_pairs = 0;
+  max_virtqueue_pairs =
+    virtio_pci_reg_read_u16 (vif, VIRTIO_MAX_VIRTQUEUE_PAIRS_OFFSET (vif));
+  u16 supported_queues = virtio_pci_modern_get_num_queues (vif);
+  virtio_log_debug (vif, "max-virtqueue-pairs %u, supported-queues %u",
+		    max_virtqueue_pairs, supported_queues);
+  return max_virtqueue_pairs;
+}
+
+static u16
+virtio_pci_modern_get_device_mtu (vlib_main_t * vm, virtio_if_t * vif)
+{
+  u16 mtu = 0;
+  mtu = virtio_pci_reg_read_u16 (vif, VIRTIO_MTU_OFFSET (vif));
+  return mtu;
+}
+
+static void
+virtio_pci_modern_read_config (vlib_main_t * vm, virtio_if_t * vif, void *dst,
+			       int len, u32 addr)
+{
+  u8 config_count;
+  do
+    {
+      config_count = virtio_pci_modern_get_config_generation (vif);
+      virtio_pci_modern_get_device_mac (vm, vif);
+      u16 status = virtio_pci_modern_get_device_status (vm, vif);
+      u16 max_queue_pairs =
+	virtio_pci_modern_get_max_virtqueue_pairs (vm, vif);
+      u16 mtu = virtio_pci_modern_get_device_mtu (vm, vif);
+      virtio_log_debug (vif, "status %u, max_queue_pairs %u, mtu %u", status,
+			max_queue_pairs, mtu);
+    }
+  while (config_count != virtio_pci_modern_get_config_generation (vif));
+}
+
+static void
+virtio_pci_modern_write_config (vlib_main_t * vm, virtio_if_t * vif,
+				void *src, int len, u32 addr)
+{
+  // do nothing
+}
+
+static u8
+virtio_pci_modern_get_isr (vlib_main_t * vm, virtio_if_t * vif)
+{
+  return virtio_pci_reg_read_u8 (vif, VIRTIO_ISR_OFFSET (vif));
+}
+
+inline void
+virtio_pci_modern_notify_queue (vlib_main_t * vm, virtio_if_t * vif,
+				u16 queue_id)
+{
+  u16 queue_notify_off =
+    virtio_pci_modern_get_queue_notify_off (vif, queue_id);
+  virtio_pci_reg_write_u16 (vif,
+			    VIRTIO_NOTIFICATION_OFFSET (vif) +
+			    vif->notify_off_multiplier * queue_notify_off,
+			    queue_id);
+}
+
+static void
+virtio_pci_modern_device_debug_config_space (vlib_main_t * vm,
+					     virtio_if_t * vif)
+{
+  // do nothing for now
+}
+
+const virtio_pci_func_t virtio_pci_modern_func = {
+  .read_config = virtio_pci_modern_read_config,
+  .write_config = virtio_pci_modern_write_config,
+  .get_device_features = virtio_pci_modern_get_device_features,
+  .get_driver_features = virtio_pci_modern_get_driver_features,
+  .set_driver_features = virtio_pci_modern_set_driver_features,
+  .get_status = virtio_pci_modern_get_status,
+  .set_status = virtio_pci_modern_set_status,
+  .device_reset = virtio_pci_modern_reset,
+  .get_isr = virtio_pci_modern_get_isr,
+  .get_queue_size = virtio_pci_modern_get_queue_size,
+  .set_queue_size = virtio_pci_modern_set_queue_size,
+  .setup_queue = virtio_pci_modern_setup_queue,
+  .del_queue = virtio_pci_modern_del_queue,
+  .notify_queue = virtio_pci_modern_notify_queue,
+  .set_config_irq = virtio_pci_modern_set_msix_config,
+  .set_queue_irq = virtio_pci_modern_set_queue_msix_vector,
+  .get_mac = virtio_pci_modern_get_device_mac,
+  .set_mac = virtio_pci_modern_set_device_mac,
+  .get_device_status = virtio_pci_modern_get_device_status,
+  .get_max_queue_pairs = virtio_pci_modern_get_max_virtqueue_pairs,
+  .get_mtu = virtio_pci_modern_get_device_mtu,
+  .device_debug_config_space = virtio_pci_modern_device_debug_config_space,
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */