/* * 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. */ #include #include #include #include #include #include #include #include void vq_init(struct virtq* vq, struct virtq_raw* raw, struct virtio_config* vio, bool is_input) { uint16_t flags = 0; if (is_input) { flags = VIRTQ_DESC_F_WRITE; } vq->raw = raw; vq->num_bufs = VQ_SIZE; for (size_t i = 0; i < vq->num_bufs; i++) { vq->raw->desc[i].flags = flags; } vq->vio = vio; } void vq_make_avail(struct virtq* vq, uint16_t desc_id) { io_write_16(&vq->raw->avail.ring[vq->raw->avail.idx % vq->num_bufs], desc_id); io_write_16(&vq->raw->avail.idx, vq->raw->avail.idx + 1); } void vq_wait(struct virtq* vq) { while (!vq_ready(vq)) { } } uint32_t vq_adv(struct virtq* vq) { return vq->raw->used.ring[vq->old_used_idx++ % vq->num_bufs].len; } void vq_set_buf_w(struct virtq* vq, uint16_t desc_id, void* data, size_t len) { vq->raw->desc[desc_id].addr = (uint64_t)data; vq->raw->desc[desc_id].len = len; assert(vq->raw->desc[desc_id].flags == VIRTQ_DESC_F_WRITE); } void vq_set_buf_r(struct virtq* vq, uint16_t desc_id, const void* data, size_t len) { vq->raw->desc[desc_id].addr = (uint64_t)data; vq->raw->desc[desc_id].len = len; assert(vq->raw->desc[desc_id].flags == 0); } ssize_t send_vq(struct virtq* vq, const char* data, size_t len) { size_t idx = vq->old_used_idx; if (len == 0) { return 0; } vq_set_buf_r(vq, idx % vq->num_bufs, data, len); vq_make_avail(vq, idx % vq->num_bufs); vq_kick(vq); vq_wait(vq); vq_set_buf_r(vq, idx % vq->num_bufs, NULL, 0); /* * QEMU's device does not set len correctly, as per the legacy-mode * notes. This means the value returned by vq_adv is unreliable, so we * assume no partial write and return len. */ vq_adv(vq); return len; } ssize_t recv_vq(struct virtq* vq, char* data, size_t len) { size_t idx = vq->old_used_idx; if (len == 0) { return 0; } vq_set_buf_w(vq, idx % vq->num_bufs, data, len); vq_make_avail(vq, idx % vq->num_bufs); vq_kick(vq); vq_wait(vq); vq_set_buf_w(vq, idx % vq->num_bufs, NULL, 0); return vq_adv(vq); }