/* * Copyright (C) 2019 The Android Open Source Project * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #pragma once #include #include #include #include #include struct virtio_config; /* * Size of virtual queues. This can be dynamic, but since we do not have * memory allocation, supporting dynamic allocation seems like overkill. * * Maximum number of ports we support in multiport of virtual serial console * device is 16. To support 16 ports, vqueue size should be double size of * maximum ports supported, since host would sent port add control event for * all ports in one sequence. */ #define VQ_SIZE 32 /** * struct virtq_desc - VirtIO buffer descriptor * @addr: Physical address of the buffer * @len: Size of the buffer * @flags: Buffer flags, see VIRTQ_DESC_* constants * @next: Used to indicate a chained buffer (not used in our code) * * Buffer descriptor, stored in a descriptor table and referenced in rings */ struct virtq_desc { uint64_t addr; uint32_t len; uint16_t flags; uint16_t next; }; /* Used to indicate a chained descriptor. We aren't doing this */ #define VIRTQ_DESC_F_NEXT (1 << 0) /* This buffer may be written by the host */ #define VIRTQ_DESC_F_WRITE (1 << 1) /* This descriptor contains other descriptor ids. We aren't doing this */ #define VIRTQ_DESC_F_INDIRECT (1 << 2) /** * struct virtq_avail - VirtIO ring of buffers for use by the device * @flags: Features for the ring. We zero this. * @idx: Location we would insert the next buffer, mod VQ_SIZE * @ring: Ring of indexes into the descriptor table. * @used_event: Delay interrupts until idx > used_event if * VIRTIO_F_EVENT_IDX was negotiated. */ struct virtq_avail { uint16_t flags; uint16_t idx; uint16_t ring[VQ_SIZE]; uint16_t used_event; }; /** * struct virtq_used_elem - What the device did with a buffer * @id: Descriptor index of the buffer used. * @len: How much of the buffer was used. * NOTE: On Legacy MMIO devices (e.g. our serial ports), this field * may be inaccurate for send requests (and is for our QEMU version). */ struct virtq_used_elem { uint32_t id; uint32_t len; }; /** * struct virtq_used - Ring of buffers used by the device * @flags: Features for the ring. We zero this. * @idx: Location the device would insert the next buffer, mod VQ_SIZE * @ring: Ring of virtq_used_elem, saying what the device has done. * @avail_event: Delay interrupt until idx > avail_event if * VIRTIO_F_EVENT_IDX was negotiated. * * While virtq_used has weaker alignment requirements (4) than PAGE_SIZE in * the current spec, the Legacy spec requires that it be aligned to PAGE_SIZE. */ struct virtq_used { uint16_t flags; uint16_t idx; struct virtq_used_elem ring[VQ_SIZE]; uint16_t avail_event; } __attribute__((aligned(PAGE_SIZE))); /** * struct virtq_raw - Legacy VirtIO layout container * @desc: Table of bufferdescriptors. Indexes/IDs mentioned elsewhere are * indexes into this table. * @avail: Ring of buffers made available to the device * @used: Ring of buffers the device is done processing * * The virtq_raw struct itself must be page aligned because a page frame is * passed to the VirtIO driver to identify the region rather than an address. * * Further, the Legacy spec requires that @avail immediately follow the * descriptor table, and that @used must be at the first page boundary * afterwards. */ struct virtq_raw { struct virtq_desc desc[VQ_SIZE]; struct virtq_avail avail; struct virtq_used used; } __attribute__((aligned(PAGE_SIZE))); ; /* VirtIO Device IDs */ #define VIRTIO_DEVICE_ID_RESERVED (0) #define VIRTIO_DEVICE_ID_BLOCK_DEVICE (2) #define VIRTIO_DEVICE_ID_CONSOLE (3) /* Flags for the status field of a VirtIO device */ /* Guest->Host: I have seen this device */ #define VIRTIO_STATUS_ACKNOWLEDGE (1) /* Guest->Host: I have a driver for this device */ #define VIRTIO_STATUS_DRIVER (2) /* Guest->Host: Driver is ready to drive the device */ #define VIRTIO_STATUS_DRIVER_OK (4) /* Guest->Host: Feature negotiation is complete */ #define VIRTIO_STATUS_FEATURES_OK (8) /* Host->Guest: The device has encountered an error; a reset may recover */ #define VIRTIO_STATUS_DEVICE_NEEDS_RESET (64) /* Guest->Host: The driver can no longer drive the device */ #define VIRTIO_STATUS_FAILED (128) /** * struct virtq - VirtIO Queue + bookkeeping information * @num_bufs: How many buffers are in the queue. For now, this is always * VQ_NUM_BUFS. * @queue_id: Which ID this queue has on the device. * @raw: The actual legacy MMIO ring. * @old_used_idx: Last seen value of the used index. * This value is used to track whether there are new buffers to * process. * @vio: Pointer to the MMIO space this virtq is used by. */ struct virtq { size_t num_bufs; size_t queue_id; struct virtq_raw* raw; uint16_t old_used_idx; struct virtio_config* vio; }; /** * virtio_set_features() - Sets the provided features on a VirtIO device * @vio: The device to set the features on * @features: The features to set */ void virtio_set_features(struct virtio_config* vio, uint64_t features); /** * virtio_get_features() - Gets the possible features on a VirtIO device * @vio: The device to query * * Return: The feature vector supported by the device */ uint64_t virtio_get_features(struct virtio_config* vio); /** * vq_init() - Initialize a virtq for the provided VirtIO device and sense. * @vq: The uninitialized virtq * @vq_raw: The uninitialized legacy-compatible VirtIO queue * @vio: The VirtIO device the queue is for * @is_input: Whether buffers moving through the queue should be * host->guest (input) or guest->host (output). * * It is reccomended that virtq_raw be statically allocated to avoid alignment * considerations. */ void vq_init(struct virtq* vq, struct virtq_raw* raw, struct virtio_config* vio, bool is_input); /** * vq_attach() - Attaches an initialized virtq to the specified queue ID * @vq: The virtq to attach * @idx: Which id to attach it on */ void vq_attach(struct virtq* vq, uint16_t idx); /** * vq_make_avail - Adds the specified descriptor to the available ring * @vq: The virtq we are operating on * @desc_id: Which descriptor to make available to the device */ void vq_make_avail(struct virtq* vq, uint16_t desc_id); /** * vq_kick - Alerts the device that this virtq has been updated * @vq: The virtq to tell the device about. */ void vq_kick(struct virtq* vq); /** * vq_ready() - Checks whether the device has processed another buffer. * @vq: The queue to check */ static inline bool vq_ready(struct virtq* vq) { return io_read_16(&vq->raw->used.idx) != vq->old_used_idx; } /** * vq_wait() - Performs a blocking wait for the device to process a buffer. * @vq: The queue to wait for processing on. */ void vq_wait(struct virtq* vq); /** * vq_adv() - Acknowledge that the host processed a buffer * @vq: The queue we are acknowledging * Return: The processed buffer's length. * This may be inaccurate for output buffers when in Legacy mode. */ uint32_t vq_adv(struct virtq* vq); /** * send_vq() - Send a buffer via a virtq. * @vq: The VirtIO queue to send on * @data: The buffer to send * @len: The size of the buffer * Return: Negative on error, size sent on success. */ ssize_t send_vq(struct virtq* vq, const char* data, size_t len); /** * recv_vq() - Receive data from a VirtIO queue. * @vq: The queue to receive on. * @data: The buffer to write to. * @len: The size of the buffer. * Return: Negative value on error, size received on success. * * Will receive *exactly* one packet (since this is not truly a stream * protocol). */ ssize_t recv_vq(struct virtq* vq, char* data, size_t len); /** * vq_set_buf_w() - Set a descriptor's buffer, host writable * @vq: The queue to operate on * @desc_id: Which descriptor to set * @data: The buffer to set it to * @len: How big the buffer is */ void vq_set_buf_w(struct virtq* vq, uint16_t desc_id, void* data, size_t len); /** * vq_set_buf_r() - Set a descriptor's buffer, host readable * @vq: The queue to operate on * @desc_id: Which descriptor to set * @data: The buffer to set it to * @len: How big the buffer is */ void vq_set_buf_r(struct virtq* vq, uint16_t desc_id, const void* data, size_t len);