| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| #include <hypervisor/pci.h> |
| #include <zircon/syscalls/hypervisor.h> |
| #include <zircon/types.h> |
| #include <virtio/virtio.h> |
| |
| // clang-format off |
| |
| // Interrupt status bits. |
| #define VIRTIO_ISR_QUEUE 0x1 |
| #define VIRTIO_ISR_DEVICE 0x2 |
| |
| // clang-format on |
| |
| __BEGIN_CDECLS |
| |
| struct vring_desc; |
| struct vring_avail; |
| struct vring_used; |
| |
| typedef struct io_apic io_apic_t; |
| typedef struct zx_vcpu_io zx_vcpu_io_t; |
| typedef struct virtio_device virtio_device_t; |
| typedef struct virtio_queue virtio_queue_t; |
| |
| /* Device-specific operations. */ |
| typedef struct virtio_device_ops { |
| // Read a device configuration field. |
| zx_status_t (*read)(const virtio_device_t* device, uint16_t port, uint8_t access_size, |
| zx_vcpu_io_t* vcpu_io); |
| |
| // Write a device configuration field. |
| zx_status_t (*write)(virtio_device_t* device, uint16_t port, const zx_vcpu_io_t* io); |
| |
| // Handle notify events for one of this devices queues. |
| zx_status_t (*queue_notify)(virtio_device_t* device, uint16_t queue_sel); |
| } virtio_device_ops_t; |
| |
| static const size_t kVirtioPciNumCapabilities = 4; |
| |
| /* Common state shared by all virtio devices. */ |
| typedef struct virtio_device { |
| mtx_t mutex; |
| |
| // Feature flags. |
| // See Virtio 1.0 Section 4.1.4.3 for more details. |
| uint32_t features; |
| uint32_t features_sel; |
| uint32_t driver_features; |
| uint32_t driver_features_sel; |
| |
| // Virtio device id. |
| uint8_t device_id; |
| // Virtio status register for the device. |
| uint8_t status; |
| // Interrupt status register. |
| uint8_t isr_status; |
| // Currently selected queue. |
| uint16_t queue_sel; |
| // Number of bytes used for this devices configuration space. |
| // |
| // This should cover only bytes used for the device-specific portions of |
| // the configuration header, omitting any of the (transport-specific) |
| // shared configuration space. |
| uint32_t config_size; |
| // Size of queues array. |
| uint16_t num_queues; |
| // Virtqueues for this device. |
| virtio_queue_t* queues; |
| |
| // Address of guest physical memory. |
| uintptr_t guest_physmem_addr; |
| // Size of guest physical memory. |
| size_t guest_physmem_size; |
| |
| // Device-specific operations. |
| const virtio_device_ops_t* ops; |
| // Private pointer for use by the device implementation. |
| void* impl; |
| |
| // PCI device for the virtio-pci transport. |
| pci_device_t pci_device; |
| |
| // We need one of these for every virtio_pci_cap_t structure we expose. |
| pci_cap_t capabilities[kVirtioPciNumCapabilities]; |
| |
| // Virtio PCI capabilities. |
| virtio_pci_cap_t common_cfg_cap; |
| virtio_pci_cap_t device_cfg_cap; |
| virtio_pci_notify_cap_t notify_cfg_cap; |
| virtio_pci_cap_t isr_cfg_cap; |
| } virtio_device_t; |
| |
| /* Configures a device for Virtio PCI functionality. |
| * |
| * Should be invoked after the rest of the virtio_device_t structure has |
| * already been intialized as the PCI configuration depends on some of the |
| * virtio device attributes. |
| */ |
| void virtio_pci_init(virtio_device_t* device); |
| |
| /* Send an interrupt back to the guest for a device. */ |
| zx_status_t virtio_device_notify(virtio_device_t* device); |
| |
| /* Handle kicks from the guest to process a queue. */ |
| zx_status_t virtio_device_kick(virtio_device_t* device, uint16_t queue_sel); |
| |
| /* Stores the Virtio queue based on the ring provided by the guest. |
| * |
| * NOTE(abdulla): This structure points to guest-controlled memory. |
| */ |
| typedef struct virtio_queue { |
| mtx_t mutex; |
| // Allow threads to block on buffers in the avail ring. |
| cnd_t avail_ring_cnd; |
| |
| // Queue addresses as defined in Virtio 1.0 Section 4.1.4.3. |
| union { |
| struct { |
| uint64_t desc; |
| uint64_t avail; |
| uint64_t used; |
| }; |
| |
| // Software will access these using 32 bit operations. Provide a |
| // convenience interface for these use cases. |
| uint32_t words[6]; |
| } addr; |
| |
| // Number of entries in the descriptor table. |
| uint16_t size; |
| uint16_t index; |
| |
| // Pointer to the owning device. |
| virtio_device_t* virtio_device; |
| |
| volatile struct vring_desc* desc; // guest-controlled |
| |
| volatile struct vring_avail* avail; // guest-controlled |
| volatile uint16_t* used_event; // guest-controlled |
| |
| volatile struct vring_used* used; // guest-controlled |
| volatile uint16_t* avail_event; // guest-controlled |
| } virtio_queue_t; |
| |
| /* Callback function for virtio_queue_handler. |
| * |
| * For chained buffers uing VRING_DESC_F_NEXT, this function will be called once for each buffer |
| * in the chain. |
| * |
| * addr - Pointer to the descriptor buffer. |
| * len - Length of the descriptor buffer. |
| * flags - Flags from the vring descriptor. |
| * used - To be incremented by the number of bytes used from addr. |
| * ctx - The same pointer passed to virtio_queue_handler. |
| */ |
| typedef zx_status_t (*virtio_queue_fn_t)(void* addr, uint32_t len, uint16_t flags, uint32_t* used, |
| void* ctx); |
| |
| /* Handles the next available descriptor in a Virtio queue, calling handler to |
| * process individual payload buffers. |
| * |
| * On success the function either returns ZX_OK if there are no more descriptors |
| * available, or ZX_ERR_NEXT if there are more available descriptors to process. |
| */ |
| zx_status_t virtio_queue_handler(virtio_queue_t* queue, virtio_queue_fn_t handler, void* ctx); |
| |
| /* Get the index of the next descriptor in the available ring. |
| * |
| * If a buffer is a available, the descriptor index is written to |index|, the |
| * queue index pointer is incremented, and ZX_OK is returned. |
| * |
| * If no buffers are available ZX_ERR_NOT_FOUND is returned. |
| */ |
| zx_status_t virtio_queue_next_avail(virtio_queue_t* queue, uint16_t* index); |
| |
| /* Blocking variant of virtio_queue_next_avail. */ |
| void virtio_queue_wait(virtio_queue_t* queue, uint16_t* index); |
| |
| /* Notify waiting threads blocked on |virtio_queue_wait| that the avail ring |
| * has descriptors available. */ |
| void virtio_queue_signal(virtio_queue_t* queue); |
| |
| /* Sets the address of the descriptor table for this queue. */ |
| void virtio_queue_set_desc_addr(virtio_queue_t* queue, uint64_t desc_addr); |
| |
| /* Sets the address of the available ring for this queue. */ |
| void virtio_queue_set_avail_addr(virtio_queue_t* queue, uint64_t avail_addr); |
| |
| /* Sets the address of the used ring for this queue. */ |
| void virtio_queue_set_used_addr(virtio_queue_t* queue, uint64_t used_addr); |
| |
| /* Callback for virtio_queue_poll. |
| * |
| * queue - The queue being polled. |
| * head - Descriptor index of the buffer chain to process. |
| * used - To be incremented by the number of bytes used from addr. |
| * ctx - The same pointer passed to virtio_queue_poll. |
| * |
| * The queue will continue to be polled as long as this method returns ZX_OK. |
| * The error ZX_ERR_STOP will be treated as a special value to indicate queue |
| * polling should stop gracefully and terminate the thread. |
| * |
| * Any other error values will be treated as unexpected errors that will cause |
| * the polling thread to be terminated with a non-zero exit value. |
| */ |
| typedef zx_status_t (*virtio_queue_poll_fn_t)(virtio_queue_t* queue, uint16_t head, |
| uint32_t* used, void* ctx); |
| |
| /* Spawn a thread to wait for descriptors to be available and invoke the |
| * provided handler on each available buffer asyncronously. |
| */ |
| zx_status_t virtio_queue_poll(virtio_queue_t* queue, virtio_queue_poll_fn_t handler, void* ctx); |
| |
| /* A higher-level API for vring_desc. */ |
| typedef struct virtio_desc { |
| // Pointer to the buffer in our address space. |
| void* addr; |
| // Number of bytes at addr. |
| uint32_t len; |
| // Is there another buffer after this one? |
| bool has_next; |
| // Only valid if has_next is true. |
| uint16_t next; |
| // If true, this buffer must only be written to (no reads). Otherwise this |
| // buffer must only be read from (no writes). |
| bool writable; |
| } virtio_desc_t; |
| |
| /* Reads a single descriptor from the queue. |
| * |
| * This method should only be called using descriptor indicies acquired with |
| * virtio_queue_next_avail (including any chained decriptors) and before |
| * they've been released with virtio_queue_return. |
| */ |
| zx_status_t virtio_queue_read_desc(virtio_queue_t* queue, uint16_t index, virtio_desc_t* desc); |
| |
| /* Return a descriptor to the used ring. |
| * |
| * |index| must be a value received from a call to virtio_queue_next_avail. Any |
| * buffers accessed via |index| or any chained descriptors must not be used |
| * after calling virtio_queue_return. |
| */ |
| void virtio_queue_return(virtio_queue_t* queue, uint16_t index, uint32_t len); |
| |
| __END_CDECLS |