blob: 7472adacf59f2f5c1eecbc84d0c7a77fb219964f [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.
*/
#include <arch/io-mem.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <test-runner-arch.h>
#include <trusty/sysdeps.h>
#include <virtio-device.h>
#include <virtio.h>
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);
}