/* * 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 (vlib_main_t * vm, 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) { u64 desc, avail, used; 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); if (vif->is_packed) { virtio_vring_t *vring = (virtio_vring_t *) p; desc = vlib_physmem_get_pa (vm, vring->packed_desc); avail = vlib_physmem_get_pa (vm, vring->driver_event); used = vlib_physmem_get_pa (vm, vring->device_event); } else { vring_t vr; vring_init (&vr, queue_size, p, VIRTIO_PCI_VRING_ALIGN); desc = vlib_physmem_get_pa (vm, vr.desc); avail = vlib_physmem_get_pa (vm, vr.avail); used = vlib_physmem_get_pa (vm, vr.used); } virtio_pci_modern_set_queue_desc (vif, desc); if (desc != virtio_pci_modern_get_queue_desc (vif)) return 1; virtio_pci_modern_set_queue_driver (vif, avail); if (avail != virtio_pci_modern_get_queue_driver (vif)) return 1; 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) { vif->mac_addr32 = virtio_pci_reg_read_u32 (vif, VIRTIO_MAC_OFFSET (vif)); vif->mac_addr16 = 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), vif->mac_addr32); virtio_pci_reg_write_u16 (vif, VIRTIO_MAC_OFFSET (vif) + 4, vif->mac_addr16); } 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_reg_write_u16 (vif, VIRTIO_NOTIFICATION_OFFSET (vif) + 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, .get_queue_notify_off = virtio_pci_modern_get_queue_notify_off, .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: */