diff options
Diffstat (limited to 'src/vnet/devices/virtio/virtio_pci_modern.c')
-rw-r--r-- | src/vnet/devices/virtio/virtio_pci_modern.c | 440 |
1 files changed, 440 insertions, 0 deletions
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 00000000000..24e8a4fe404 --- /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: + */ |