blob: c4ed29359a5c49cf5dbe3e42d02c620b7c8eba15 [file] [log] [blame]
/*
* 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 <arch/io-mem.h>
#include <arch/virtio-base.h>
#include <stdbool.h>
#include <sys/types.h>
#include <trusty/sysdeps.h>
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);