mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 21:40:49 +00:00
5442b854ea
This patch adds the command payload decoding and introduces the functions that will do the actual command handling. Those functions are not yet implemented. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Reviewed-by: Peter Xu <peterx@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Message-Id: <20200214132745.23392-3-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
318 lines
10 KiB
C
318 lines
10 KiB
C
/*
|
|
* virtio-iommu device
|
|
*
|
|
* Copyright (c) 2020 Red Hat, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2 or later, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/iov.h"
|
|
#include "qemu-common.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "hw/virtio/virtio.h"
|
|
#include "sysemu/kvm.h"
|
|
#include "trace.h"
|
|
|
|
#include "standard-headers/linux/virtio_ids.h"
|
|
|
|
#include "hw/virtio/virtio-bus.h"
|
|
#include "hw/virtio/virtio-access.h"
|
|
#include "hw/virtio/virtio-iommu.h"
|
|
|
|
/* Max size */
|
|
#define VIOMMU_DEFAULT_QUEUE_SIZE 256
|
|
|
|
static int virtio_iommu_attach(VirtIOIOMMU *s,
|
|
struct virtio_iommu_req_attach *req)
|
|
{
|
|
uint32_t domain_id = le32_to_cpu(req->domain);
|
|
uint32_t ep_id = le32_to_cpu(req->endpoint);
|
|
|
|
trace_virtio_iommu_attach(domain_id, ep_id);
|
|
|
|
return VIRTIO_IOMMU_S_UNSUPP;
|
|
}
|
|
|
|
static int virtio_iommu_detach(VirtIOIOMMU *s,
|
|
struct virtio_iommu_req_detach *req)
|
|
{
|
|
uint32_t domain_id = le32_to_cpu(req->domain);
|
|
uint32_t ep_id = le32_to_cpu(req->endpoint);
|
|
|
|
trace_virtio_iommu_detach(domain_id, ep_id);
|
|
|
|
return VIRTIO_IOMMU_S_UNSUPP;
|
|
}
|
|
|
|
static int virtio_iommu_map(VirtIOIOMMU *s,
|
|
struct virtio_iommu_req_map *req)
|
|
{
|
|
uint32_t domain_id = le32_to_cpu(req->domain);
|
|
uint64_t phys_start = le64_to_cpu(req->phys_start);
|
|
uint64_t virt_start = le64_to_cpu(req->virt_start);
|
|
uint64_t virt_end = le64_to_cpu(req->virt_end);
|
|
uint32_t flags = le32_to_cpu(req->flags);
|
|
|
|
trace_virtio_iommu_map(domain_id, virt_start, virt_end, phys_start, flags);
|
|
|
|
return VIRTIO_IOMMU_S_UNSUPP;
|
|
}
|
|
|
|
static int virtio_iommu_unmap(VirtIOIOMMU *s,
|
|
struct virtio_iommu_req_unmap *req)
|
|
{
|
|
uint32_t domain_id = le32_to_cpu(req->domain);
|
|
uint64_t virt_start = le64_to_cpu(req->virt_start);
|
|
uint64_t virt_end = le64_to_cpu(req->virt_end);
|
|
|
|
trace_virtio_iommu_unmap(domain_id, virt_start, virt_end);
|
|
|
|
return VIRTIO_IOMMU_S_UNSUPP;
|
|
}
|
|
|
|
static int virtio_iommu_iov_to_req(struct iovec *iov,
|
|
unsigned int iov_cnt,
|
|
void *req, size_t req_sz)
|
|
{
|
|
size_t sz, payload_sz = req_sz - sizeof(struct virtio_iommu_req_tail);
|
|
|
|
sz = iov_to_buf(iov, iov_cnt, 0, req, payload_sz);
|
|
if (unlikely(sz != payload_sz)) {
|
|
return VIRTIO_IOMMU_S_INVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define virtio_iommu_handle_req(__req) \
|
|
static int virtio_iommu_handle_ ## __req(VirtIOIOMMU *s, \
|
|
struct iovec *iov, \
|
|
unsigned int iov_cnt) \
|
|
{ \
|
|
struct virtio_iommu_req_ ## __req req; \
|
|
int ret = virtio_iommu_iov_to_req(iov, iov_cnt, &req, sizeof(req)); \
|
|
\
|
|
return ret ? ret : virtio_iommu_ ## __req(s, &req); \
|
|
}
|
|
|
|
virtio_iommu_handle_req(attach)
|
|
virtio_iommu_handle_req(detach)
|
|
virtio_iommu_handle_req(map)
|
|
virtio_iommu_handle_req(unmap)
|
|
|
|
static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq)
|
|
{
|
|
VirtIOIOMMU *s = VIRTIO_IOMMU(vdev);
|
|
struct virtio_iommu_req_head head;
|
|
struct virtio_iommu_req_tail tail = {};
|
|
VirtQueueElement *elem;
|
|
unsigned int iov_cnt;
|
|
struct iovec *iov;
|
|
size_t sz;
|
|
|
|
for (;;) {
|
|
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
|
|
if (!elem) {
|
|
return;
|
|
}
|
|
|
|
if (iov_size(elem->in_sg, elem->in_num) < sizeof(tail) ||
|
|
iov_size(elem->out_sg, elem->out_num) < sizeof(head)) {
|
|
virtio_error(vdev, "virtio-iommu bad head/tail size");
|
|
virtqueue_detach_element(vq, elem, 0);
|
|
g_free(elem);
|
|
break;
|
|
}
|
|
|
|
iov_cnt = elem->out_num;
|
|
iov = elem->out_sg;
|
|
sz = iov_to_buf(iov, iov_cnt, 0, &head, sizeof(head));
|
|
if (unlikely(sz != sizeof(head))) {
|
|
tail.status = VIRTIO_IOMMU_S_DEVERR;
|
|
goto out;
|
|
}
|
|
qemu_mutex_lock(&s->mutex);
|
|
switch (head.type) {
|
|
case VIRTIO_IOMMU_T_ATTACH:
|
|
tail.status = virtio_iommu_handle_attach(s, iov, iov_cnt);
|
|
break;
|
|
case VIRTIO_IOMMU_T_DETACH:
|
|
tail.status = virtio_iommu_handle_detach(s, iov, iov_cnt);
|
|
break;
|
|
case VIRTIO_IOMMU_T_MAP:
|
|
tail.status = virtio_iommu_handle_map(s, iov, iov_cnt);
|
|
break;
|
|
case VIRTIO_IOMMU_T_UNMAP:
|
|
tail.status = virtio_iommu_handle_unmap(s, iov, iov_cnt);
|
|
break;
|
|
default:
|
|
tail.status = VIRTIO_IOMMU_S_UNSUPP;
|
|
}
|
|
qemu_mutex_unlock(&s->mutex);
|
|
|
|
out:
|
|
sz = iov_from_buf(elem->in_sg, elem->in_num, 0,
|
|
&tail, sizeof(tail));
|
|
assert(sz == sizeof(tail));
|
|
|
|
virtqueue_push(vq, elem, sizeof(tail));
|
|
virtio_notify(vdev, vq);
|
|
g_free(elem);
|
|
}
|
|
}
|
|
|
|
static void virtio_iommu_get_config(VirtIODevice *vdev, uint8_t *config_data)
|
|
{
|
|
VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev);
|
|
struct virtio_iommu_config *config = &dev->config;
|
|
|
|
trace_virtio_iommu_get_config(config->page_size_mask,
|
|
config->input_range.start,
|
|
config->input_range.end,
|
|
config->domain_range.end,
|
|
config->probe_size);
|
|
memcpy(config_data, &dev->config, sizeof(struct virtio_iommu_config));
|
|
}
|
|
|
|
static void virtio_iommu_set_config(VirtIODevice *vdev,
|
|
const uint8_t *config_data)
|
|
{
|
|
struct virtio_iommu_config config;
|
|
|
|
memcpy(&config, config_data, sizeof(struct virtio_iommu_config));
|
|
trace_virtio_iommu_set_config(config.page_size_mask,
|
|
config.input_range.start,
|
|
config.input_range.end,
|
|
config.domain_range.end,
|
|
config.probe_size);
|
|
}
|
|
|
|
static uint64_t virtio_iommu_get_features(VirtIODevice *vdev, uint64_t f,
|
|
Error **errp)
|
|
{
|
|
VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev);
|
|
|
|
f |= dev->features;
|
|
trace_virtio_iommu_get_features(f);
|
|
return f;
|
|
}
|
|
|
|
/*
|
|
* Migration is not yet supported: most of the state consists
|
|
* of balanced binary trees which are not yet ready for getting
|
|
* migrated
|
|
*/
|
|
static const VMStateDescription vmstate_virtio_iommu_device = {
|
|
.name = "virtio-iommu-device",
|
|
.unmigratable = 1,
|
|
};
|
|
|
|
static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
|
VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
|
|
|
|
virtio_init(vdev, "virtio-iommu", VIRTIO_ID_IOMMU,
|
|
sizeof(struct virtio_iommu_config));
|
|
|
|
s->req_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE,
|
|
virtio_iommu_handle_command);
|
|
s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL);
|
|
|
|
s->config.page_size_mask = TARGET_PAGE_MASK;
|
|
s->config.input_range.end = -1UL;
|
|
s->config.domain_range.end = 32;
|
|
|
|
virtio_add_feature(&s->features, VIRTIO_RING_F_EVENT_IDX);
|
|
virtio_add_feature(&s->features, VIRTIO_RING_F_INDIRECT_DESC);
|
|
virtio_add_feature(&s->features, VIRTIO_F_VERSION_1);
|
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_INPUT_RANGE);
|
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_DOMAIN_RANGE);
|
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
|
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
|
|
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO);
|
|
|
|
qemu_mutex_init(&s->mutex);
|
|
}
|
|
|
|
static void virtio_iommu_device_unrealize(DeviceState *dev, Error **errp)
|
|
{
|
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
|
|
|
virtio_cleanup(vdev);
|
|
}
|
|
|
|
static void virtio_iommu_device_reset(VirtIODevice *vdev)
|
|
{
|
|
trace_virtio_iommu_device_reset();
|
|
}
|
|
|
|
static void virtio_iommu_set_status(VirtIODevice *vdev, uint8_t status)
|
|
{
|
|
trace_virtio_iommu_device_status(status);
|
|
}
|
|
|
|
static void virtio_iommu_instance_init(Object *obj)
|
|
{
|
|
}
|
|
|
|
static const VMStateDescription vmstate_virtio_iommu = {
|
|
.name = "virtio-iommu",
|
|
.minimum_version_id = 1,
|
|
.version_id = 1,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_VIRTIO_DEVICE,
|
|
VMSTATE_END_OF_LIST()
|
|
},
|
|
};
|
|
|
|
static Property virtio_iommu_properties[] = {
|
|
DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, "PCI", PCIBus *),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void virtio_iommu_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
|
|
|
device_class_set_props(dc, virtio_iommu_properties);
|
|
dc->vmsd = &vmstate_virtio_iommu;
|
|
|
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
|
vdc->realize = virtio_iommu_device_realize;
|
|
vdc->unrealize = virtio_iommu_device_unrealize;
|
|
vdc->reset = virtio_iommu_device_reset;
|
|
vdc->get_config = virtio_iommu_get_config;
|
|
vdc->set_config = virtio_iommu_set_config;
|
|
vdc->get_features = virtio_iommu_get_features;
|
|
vdc->set_status = virtio_iommu_set_status;
|
|
vdc->vmsd = &vmstate_virtio_iommu_device;
|
|
}
|
|
|
|
static const TypeInfo virtio_iommu_info = {
|
|
.name = TYPE_VIRTIO_IOMMU,
|
|
.parent = TYPE_VIRTIO_DEVICE,
|
|
.instance_size = sizeof(VirtIOIOMMU),
|
|
.instance_init = virtio_iommu_instance_init,
|
|
.class_init = virtio_iommu_class_init,
|
|
};
|
|
|
|
static void virtio_register_types(void)
|
|
{
|
|
type_register_static(&virtio_iommu_info);
|
|
}
|
|
|
|
type_init(virtio_register_types)
|