virtio, pc: fixes, features
New virtio iommu. Unrealize memory leaks. In-band kick/call support. Bugfixes, documentation all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAl5XgekPHG1zdEByZWRo YXQuY29tAAoJECgfDbjSjVRpPe0IAJzRlUZMmT0pJ0ppCfydAlnChGyoOmm5BnuV 1u0qSxDYv3qDmIHa+LVcAwJCc4OmmWzFgWiO2V2+vnjwu/RwsiwzZOzXwecRnlsn 0OjROmROAyR5j8h6pSzinWyRLcaKSS8tasDMRbRh7wlkEns78970V5GBPnvVQsGt WG2BO8cvkoCksry16YnzPQEuQ055q1x19rsw2yeZ+3yVfLtiSoplxo5/7UAIGcaE K4zUTQ3ktAbYfKxE96t7rxlmjbFM8H/W0GvKaPqjBDHEoi0SN+uIpyh5rHSeSsp8 WS4KUMFvr/z5eEsD02bxsA87nC2PDeTWEgOO/QyBUMtgUt6i274= =ue55 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging virtio, pc: fixes, features New virtio iommu. Unrealize memory leaks. In-band kick/call support. Bugfixes, documentation all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Thu 27 Feb 2020 08:46:33 GMT # gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469 # gpg: issuer "mst@redhat.com" # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full] # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [full] # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * remotes/mst/tags/for_upstream: (30 commits) Fixed assert in vhost_user_set_mem_table_postcopy vhost-user: only set slave channel for first vq acpi: cpuhp: document CPHP_GET_CPU_ID_CMD command libvhost-user: implement in-band notifications docs: vhost-user: add in-band kick/call messages libvhost-user: handle NOFD flag in call/kick/err better libvhost-user-glib: use g_main_context_get_thread_default() libvhost-user-glib: fix VugDev main fd cleanup libvhost-user: implement VHOST_USER_PROTOCOL_F_REPLY_ACK MAINTAINERS: add virtio-iommu related files hw/arm/virt: Add the virtio-iommu device tree mappings virtio-iommu-pci: Add virtio iommu pci support virtio-iommu: Support migration virtio-iommu: Implement fault reporting virtio-iommu: Implement translate virtio-iommu: Implement map/unmap virtio-iommu: Implement attach/detach command virtio-iommu: Decode the command payload virtio-iommu: Add skeleton virtio: gracefully handle invalid region caches ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
8b6b68e05b
@ -1639,6 +1639,12 @@ F: hw/input/virtio-input*.c
|
|||||||
F: include/hw/virtio/virtio-input.h
|
F: include/hw/virtio/virtio-input.h
|
||||||
F: contrib/vhost-user-input/*
|
F: contrib/vhost-user-input/*
|
||||||
|
|
||||||
|
virtio-iommu
|
||||||
|
M: Eric Auger <eric.auger@redhat.com>
|
||||||
|
S: Maintained
|
||||||
|
F: hw/virtio/virtio-iommu*.c
|
||||||
|
F: include/hw/virtio/virtio-iommu.h
|
||||||
|
|
||||||
virtio-serial
|
virtio-serial
|
||||||
M: Laurent Vivier <lvivier@redhat.com>
|
M: Laurent Vivier <lvivier@redhat.com>
|
||||||
R: Amit Shah <amit@kernel.org>
|
R: Amit Shah <amit@kernel.org>
|
||||||
|
@ -89,9 +89,8 @@ vug_source_new(VugDev *gdev, int fd, GIOCondition cond,
|
|||||||
src->gfd.events = cond;
|
src->gfd.events = cond;
|
||||||
|
|
||||||
g_source_add_poll(gsrc, &src->gfd);
|
g_source_add_poll(gsrc, &src->gfd);
|
||||||
id = g_source_attach(gsrc, NULL);
|
id = g_source_attach(gsrc, g_main_context_get_thread_default());
|
||||||
g_assert(id);
|
g_assert(id);
|
||||||
g_source_unref(gsrc);
|
|
||||||
|
|
||||||
return gsrc;
|
return gsrc;
|
||||||
}
|
}
|
||||||
@ -131,6 +130,16 @@ static void vug_watch(VuDev *dev, int condition, void *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vug_source_destroy(GSource *src)
|
||||||
|
{
|
||||||
|
if (!src) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_source_destroy(src);
|
||||||
|
g_source_unref(src);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vug_init(VugDev *dev, uint16_t max_queues, int socket,
|
vug_init(VugDev *dev, uint16_t max_queues, int socket,
|
||||||
vu_panic_cb panic, const VuDevIface *iface)
|
vu_panic_cb panic, const VuDevIface *iface)
|
||||||
@ -144,7 +153,7 @@ vug_init(VugDev *dev, uint16_t max_queues, int socket,
|
|||||||
}
|
}
|
||||||
|
|
||||||
dev->fdmap = g_hash_table_new_full(NULL, NULL, NULL,
|
dev->fdmap = g_hash_table_new_full(NULL, NULL, NULL,
|
||||||
(GDestroyNotify) g_source_destroy);
|
(GDestroyNotify) vug_source_destroy);
|
||||||
|
|
||||||
dev->src = vug_source_new(dev, socket, G_IO_IN, vug_watch, NULL);
|
dev->src = vug_source_new(dev, socket, G_IO_IN, vug_watch, NULL);
|
||||||
|
|
||||||
@ -157,5 +166,5 @@ vug_deinit(VugDev *dev)
|
|||||||
g_assert(dev);
|
g_assert(dev);
|
||||||
|
|
||||||
g_hash_table_unref(dev->fdmap);
|
g_hash_table_unref(dev->fdmap);
|
||||||
g_source_unref(dev->src);
|
vug_source_destroy(dev->src);
|
||||||
}
|
}
|
||||||
|
@ -31,5 +31,6 @@ void vug_deinit(VugDev *dev);
|
|||||||
|
|
||||||
GSource *vug_source_new(VugDev *dev, int fd, GIOCondition cond,
|
GSource *vug_source_new(VugDev *dev, int fd, GIOCondition cond,
|
||||||
vu_watch_cb vu_cb, gpointer data);
|
vu_watch_cb vu_cb, gpointer data);
|
||||||
|
void vug_source_destroy(GSource *src);
|
||||||
|
|
||||||
#endif /* LIBVHOST_USER_GLIB_H */
|
#endif /* LIBVHOST_USER_GLIB_H */
|
||||||
|
@ -136,6 +136,7 @@ vu_request_to_string(unsigned int req)
|
|||||||
REQ(VHOST_USER_GET_INFLIGHT_FD),
|
REQ(VHOST_USER_GET_INFLIGHT_FD),
|
||||||
REQ(VHOST_USER_SET_INFLIGHT_FD),
|
REQ(VHOST_USER_SET_INFLIGHT_FD),
|
||||||
REQ(VHOST_USER_GPU_SET_SOCKET),
|
REQ(VHOST_USER_GPU_SET_SOCKET),
|
||||||
|
REQ(VHOST_USER_VRING_KICK),
|
||||||
REQ(VHOST_USER_MAX),
|
REQ(VHOST_USER_MAX),
|
||||||
};
|
};
|
||||||
#undef REQ
|
#undef REQ
|
||||||
@ -163,7 +164,10 @@ vu_panic(VuDev *dev, const char *msg, ...)
|
|||||||
dev->panic(dev, buf);
|
dev->panic(dev, buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
/* FIXME: find a way to call virtio_error? */
|
/*
|
||||||
|
* FIXME:
|
||||||
|
* find a way to call virtio_error, or perhaps close the connection?
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Translate guest physical address to our virtual address. */
|
/* Translate guest physical address to our virtual address. */
|
||||||
@ -948,6 +952,7 @@ static bool
|
|||||||
vu_check_queue_msg_file(VuDev *dev, VhostUserMsg *vmsg)
|
vu_check_queue_msg_file(VuDev *dev, VhostUserMsg *vmsg)
|
||||||
{
|
{
|
||||||
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
||||||
|
bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK;
|
||||||
|
|
||||||
if (index >= dev->max_queues) {
|
if (index >= dev->max_queues) {
|
||||||
vmsg_close_fds(vmsg);
|
vmsg_close_fds(vmsg);
|
||||||
@ -955,8 +960,12 @@ vu_check_queue_msg_file(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK ||
|
if (nofd) {
|
||||||
vmsg->fd_num != 1) {
|
vmsg_close_fds(vmsg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vmsg->fd_num != 1) {
|
||||||
vmsg_close_fds(vmsg);
|
vmsg_close_fds(vmsg);
|
||||||
vu_panic(dev, "Invalid fds in request: %d", vmsg->request);
|
vu_panic(dev, "Invalid fds in request: %d", vmsg->request);
|
||||||
return false;
|
return false;
|
||||||
@ -1053,6 +1062,7 @@ static bool
|
|||||||
vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg)
|
vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||||
{
|
{
|
||||||
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
||||||
|
bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK;
|
||||||
|
|
||||||
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
||||||
|
|
||||||
@ -1066,8 +1076,8 @@ vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
dev->vq[index].kick_fd = -1;
|
dev->vq[index].kick_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->vq[index].kick_fd = vmsg->fds[0];
|
dev->vq[index].kick_fd = nofd ? -1 : vmsg->fds[0];
|
||||||
DPRINT("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index);
|
DPRINT("Got kick_fd: %d for vq: %d\n", dev->vq[index].kick_fd, index);
|
||||||
|
|
||||||
dev->vq[index].started = true;
|
dev->vq[index].started = true;
|
||||||
if (dev->iface->queue_set_started) {
|
if (dev->iface->queue_set_started) {
|
||||||
@ -1147,6 +1157,7 @@ static bool
|
|||||||
vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg)
|
vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||||
{
|
{
|
||||||
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
||||||
|
bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK;
|
||||||
|
|
||||||
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
||||||
|
|
||||||
@ -1159,14 +1170,14 @@ vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
dev->vq[index].call_fd = -1;
|
dev->vq[index].call_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->vq[index].call_fd = vmsg->fds[0];
|
dev->vq[index].call_fd = nofd ? -1 : vmsg->fds[0];
|
||||||
|
|
||||||
/* in case of I/O hang after reconnecting */
|
/* in case of I/O hang after reconnecting */
|
||||||
if (eventfd_write(vmsg->fds[0], 1)) {
|
if (dev->vq[index].call_fd != -1 && eventfd_write(vmsg->fds[0], 1)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINT("Got call_fd: %d for vq: %d\n", vmsg->fds[0], index);
|
DPRINT("Got call_fd: %d for vq: %d\n", dev->vq[index].call_fd, index);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1175,6 +1186,7 @@ static bool
|
|||||||
vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg)
|
vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||||
{
|
{
|
||||||
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
||||||
|
bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK;
|
||||||
|
|
||||||
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
||||||
|
|
||||||
@ -1187,7 +1199,7 @@ vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
dev->vq[index].err_fd = -1;
|
dev->vq[index].err_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->vq[index].err_fd = vmsg->fds[0];
|
dev->vq[index].err_fd = nofd ? -1 : vmsg->fds[0];
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1195,11 +1207,20 @@ vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
static bool
|
static bool
|
||||||
vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Note that we support, but intentionally do not set,
|
||||||
|
* VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS. This means that
|
||||||
|
* a device implementation can return it in its callback
|
||||||
|
* (get_protocol_features) if it wants to use this for
|
||||||
|
* simulation, but it is otherwise not desirable (if even
|
||||||
|
* implemented by the master.)
|
||||||
|
*/
|
||||||
uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_MQ |
|
uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_MQ |
|
||||||
1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD |
|
1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD |
|
||||||
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ |
|
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ |
|
||||||
1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER |
|
1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER |
|
||||||
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD;
|
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD |
|
||||||
|
1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK;
|
||||||
|
|
||||||
if (have_userfault()) {
|
if (have_userfault()) {
|
||||||
features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT;
|
features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT;
|
||||||
@ -1226,6 +1247,25 @@ vu_set_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
|
|
||||||
dev->protocol_features = vmsg->payload.u64;
|
dev->protocol_features = vmsg->payload.u64;
|
||||||
|
|
||||||
|
if (vu_has_protocol_feature(dev,
|
||||||
|
VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS) &&
|
||||||
|
(!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SLAVE_REQ) ||
|
||||||
|
!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_REPLY_ACK))) {
|
||||||
|
/*
|
||||||
|
* The use case for using messages for kick/call is simulation, to make
|
||||||
|
* the kick and call synchronous. To actually get that behaviour, both
|
||||||
|
* of the other features are required.
|
||||||
|
* Theoretically, one could use only kick messages, or do them without
|
||||||
|
* having F_REPLY_ACK, but too many (possibly pending) messages on the
|
||||||
|
* socket will eventually cause the master to hang, to avoid this in
|
||||||
|
* scenarios where not desired enforce that the settings are in a way
|
||||||
|
* that actually enables the simulation case.
|
||||||
|
*/
|
||||||
|
vu_panic(dev,
|
||||||
|
"F_IN_BAND_NOTIFICATIONS requires F_SLAVE_REQ && F_REPLY_ACK");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (dev->iface->set_protocol_features) {
|
if (dev->iface->set_protocol_features) {
|
||||||
dev->iface->set_protocol_features(dev, features);
|
dev->iface->set_protocol_features(dev, features);
|
||||||
}
|
}
|
||||||
@ -1486,6 +1526,34 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vu_handle_vring_kick(VuDev *dev, VhostUserMsg *vmsg)
|
||||||
|
{
|
||||||
|
unsigned int index = vmsg->payload.state.index;
|
||||||
|
|
||||||
|
if (index >= dev->max_queues) {
|
||||||
|
vu_panic(dev, "Invalid queue index: %u", index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINT("Got kick message: handler:%p idx:%d\n",
|
||||||
|
dev->vq[index].handler, index);
|
||||||
|
|
||||||
|
if (!dev->vq[index].started) {
|
||||||
|
dev->vq[index].started = true;
|
||||||
|
|
||||||
|
if (dev->iface->queue_set_started) {
|
||||||
|
dev->iface->queue_set_started(dev, index, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev->vq[index].handler) {
|
||||||
|
dev->vq[index].handler(dev, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
||||||
{
|
{
|
||||||
@ -1568,6 +1636,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
|||||||
return vu_get_inflight_fd(dev, vmsg);
|
return vu_get_inflight_fd(dev, vmsg);
|
||||||
case VHOST_USER_SET_INFLIGHT_FD:
|
case VHOST_USER_SET_INFLIGHT_FD:
|
||||||
return vu_set_inflight_fd(dev, vmsg);
|
return vu_set_inflight_fd(dev, vmsg);
|
||||||
|
case VHOST_USER_VRING_KICK:
|
||||||
|
return vu_handle_vring_kick(dev, vmsg);
|
||||||
default:
|
default:
|
||||||
vmsg_close_fds(vmsg);
|
vmsg_close_fds(vmsg);
|
||||||
vu_panic(dev, "Unhandled request: %d", vmsg->request);
|
vu_panic(dev, "Unhandled request: %d", vmsg->request);
|
||||||
@ -1581,13 +1651,20 @@ vu_dispatch(VuDev *dev)
|
|||||||
{
|
{
|
||||||
VhostUserMsg vmsg = { 0, };
|
VhostUserMsg vmsg = { 0, };
|
||||||
int reply_requested;
|
int reply_requested;
|
||||||
bool success = false;
|
bool need_reply, success = false;
|
||||||
|
|
||||||
if (!vu_message_read(dev, dev->sock, &vmsg)) {
|
if (!vu_message_read(dev, dev->sock, &vmsg)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
need_reply = vmsg.flags & VHOST_USER_NEED_REPLY_MASK;
|
||||||
|
|
||||||
reply_requested = vu_process_message(dev, &vmsg);
|
reply_requested = vu_process_message(dev, &vmsg);
|
||||||
|
if (!reply_requested && need_reply) {
|
||||||
|
vmsg_set_reply_u64(&vmsg, 0);
|
||||||
|
reply_requested = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!reply_requested) {
|
if (!reply_requested) {
|
||||||
success = true;
|
success = true;
|
||||||
goto end;
|
goto end;
|
||||||
@ -2022,8 +2099,7 @@ vring_notify(VuDev *dev, VuVirtq *vq)
|
|||||||
return !v || vring_need_event(vring_get_used_event(vq), new, old);
|
return !v || vring_need_event(vring_get_used_event(vq), new, old);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void _vu_queue_notify(VuDev *dev, VuVirtq *vq, bool sync)
|
||||||
vu_queue_notify(VuDev *dev, VuVirtq *vq)
|
|
||||||
{
|
{
|
||||||
if (unlikely(dev->broken) ||
|
if (unlikely(dev->broken) ||
|
||||||
unlikely(!vq->vring.avail)) {
|
unlikely(!vq->vring.avail)) {
|
||||||
@ -2035,11 +2111,48 @@ vu_queue_notify(VuDev *dev, VuVirtq *vq)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vq->call_fd < 0 &&
|
||||||
|
vu_has_protocol_feature(dev,
|
||||||
|
VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS) &&
|
||||||
|
vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SLAVE_REQ)) {
|
||||||
|
VhostUserMsg vmsg = {
|
||||||
|
.request = VHOST_USER_SLAVE_VRING_CALL,
|
||||||
|
.flags = VHOST_USER_VERSION,
|
||||||
|
.size = sizeof(vmsg.payload.state),
|
||||||
|
.payload.state = {
|
||||||
|
.index = vq - dev->vq,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
bool ack = sync &&
|
||||||
|
vu_has_protocol_feature(dev,
|
||||||
|
VHOST_USER_PROTOCOL_F_REPLY_ACK);
|
||||||
|
|
||||||
|
if (ack) {
|
||||||
|
vmsg.flags |= VHOST_USER_NEED_REPLY_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
vu_message_write(dev, dev->slave_fd, &vmsg);
|
||||||
|
if (ack) {
|
||||||
|
vu_message_read(dev, dev->slave_fd, &vmsg);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (eventfd_write(vq->call_fd, 1) < 0) {
|
if (eventfd_write(vq->call_fd, 1) < 0) {
|
||||||
vu_panic(dev, "Error writing eventfd: %s", strerror(errno));
|
vu_panic(dev, "Error writing eventfd: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vu_queue_notify(VuDev *dev, VuVirtq *vq)
|
||||||
|
{
|
||||||
|
_vu_queue_notify(dev, vq, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vu_queue_notify_sync(VuDev *dev, VuVirtq *vq)
|
||||||
|
{
|
||||||
|
_vu_queue_notify(dev, vq, true);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
vring_used_flags_set_bit(VuVirtq *vq, int mask)
|
vring_used_flags_set_bit(VuVirtq *vq, int mask)
|
||||||
{
|
{
|
||||||
|
@ -54,6 +54,7 @@ enum VhostUserProtocolFeature {
|
|||||||
VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10,
|
VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10,
|
||||||
VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11,
|
VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11,
|
||||||
VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12,
|
VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12,
|
||||||
|
VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS = 14,
|
||||||
|
|
||||||
VHOST_USER_PROTOCOL_F_MAX
|
VHOST_USER_PROTOCOL_F_MAX
|
||||||
};
|
};
|
||||||
@ -95,6 +96,7 @@ typedef enum VhostUserRequest {
|
|||||||
VHOST_USER_GET_INFLIGHT_FD = 31,
|
VHOST_USER_GET_INFLIGHT_FD = 31,
|
||||||
VHOST_USER_SET_INFLIGHT_FD = 32,
|
VHOST_USER_SET_INFLIGHT_FD = 32,
|
||||||
VHOST_USER_GPU_SET_SOCKET = 33,
|
VHOST_USER_GPU_SET_SOCKET = 33,
|
||||||
|
VHOST_USER_VRING_KICK = 35,
|
||||||
VHOST_USER_MAX
|
VHOST_USER_MAX
|
||||||
} VhostUserRequest;
|
} VhostUserRequest;
|
||||||
|
|
||||||
@ -103,6 +105,8 @@ typedef enum VhostUserSlaveRequest {
|
|||||||
VHOST_USER_SLAVE_IOTLB_MSG = 1,
|
VHOST_USER_SLAVE_IOTLB_MSG = 1,
|
||||||
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
|
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
|
||||||
VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
|
VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
|
||||||
|
VHOST_USER_SLAVE_VRING_CALL = 4,
|
||||||
|
VHOST_USER_SLAVE_VRING_ERR = 5,
|
||||||
VHOST_USER_SLAVE_MAX
|
VHOST_USER_SLAVE_MAX
|
||||||
} VhostUserSlaveRequest;
|
} VhostUserSlaveRequest;
|
||||||
|
|
||||||
@ -528,6 +532,16 @@ bool vu_queue_empty(VuDev *dev, VuVirtq *vq);
|
|||||||
*/
|
*/
|
||||||
void vu_queue_notify(VuDev *dev, VuVirtq *vq);
|
void vu_queue_notify(VuDev *dev, VuVirtq *vq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vu_queue_notify_sync:
|
||||||
|
* @dev: a VuDev context
|
||||||
|
* @vq: a VuVirtq queue
|
||||||
|
*
|
||||||
|
* Request to notify the queue via callfd (skipped if unnecessary)
|
||||||
|
* or sync message if possible.
|
||||||
|
*/
|
||||||
|
void vu_queue_notify_sync(VuDev *dev, VuVirtq *vq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vu_queue_pop:
|
* vu_queue_pop:
|
||||||
* @dev: a VuDev context
|
* @dev: a VuDev context
|
||||||
|
@ -187,7 +187,7 @@ vi_queue_set_started(VuDev *dev, int qidx, bool started)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!started && vi->evsrc) {
|
if (!started && vi->evsrc) {
|
||||||
g_source_destroy(vi->evsrc);
|
vug_source_destroy(vi->evsrc);
|
||||||
vi->evsrc = NULL;
|
vi->evsrc = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,9 +401,7 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
vug_deinit(&vi.dev);
|
vug_deinit(&vi.dev);
|
||||||
|
|
||||||
if (vi.evsrc) {
|
vug_source_destroy(vi.evsrc);
|
||||||
g_source_unref(vi.evsrc);
|
|
||||||
}
|
|
||||||
g_array_free(vi.config, TRUE);
|
g_array_free(vi.config, TRUE);
|
||||||
g_free(vi.queue);
|
g_free(vi.queue);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
Vhost-user Protocol
|
Vhost-user Protocol
|
||||||
===================
|
===================
|
||||||
:Copyright: 2014 Virtual Open Systems Sarl.
|
:Copyright: 2014 Virtual Open Systems Sarl.
|
||||||
|
:Copyright: 2019 Intel Corporation
|
||||||
:Licence: This work is licensed under the terms of the GNU GPL,
|
:Licence: This work is licensed under the terms of the GNU GPL,
|
||||||
version 2 or later. See the COPYING file in the top-level
|
version 2 or later. See the COPYING file in the top-level
|
||||||
directory.
|
directory.
|
||||||
@ -279,6 +280,9 @@ If *master* is unable to send the full message or receives a wrong
|
|||||||
reply it will close the connection. An optional reconnection mechanism
|
reply it will close the connection. An optional reconnection mechanism
|
||||||
can be implemented.
|
can be implemented.
|
||||||
|
|
||||||
|
If *slave* detects some error such as incompatible features, it may also
|
||||||
|
close the connection. This should only happen in exceptional circumstances.
|
||||||
|
|
||||||
Any protocol extensions are gated by protocol feature bits, which
|
Any protocol extensions are gated by protocol feature bits, which
|
||||||
allows full backwards compatibility on both master and slave. As
|
allows full backwards compatibility on both master and slave. As
|
||||||
older slaves don't support negotiating protocol features, a feature
|
older slaves don't support negotiating protocol features, a feature
|
||||||
@ -315,7 +319,8 @@ it until ring is started, or after it has been stopped.
|
|||||||
|
|
||||||
Client must start ring upon receiving a kick (that is, detecting that
|
Client must start ring upon receiving a kick (that is, detecting that
|
||||||
file descriptor is readable) on the descriptor specified by
|
file descriptor is readable) on the descriptor specified by
|
||||||
``VHOST_USER_SET_VRING_KICK``, and stop ring upon receiving
|
``VHOST_USER_SET_VRING_KICK`` or receiving the in-band message
|
||||||
|
``VHOST_USER_VRING_KICK`` if negotiated, and stop ring upon receiving
|
||||||
``VHOST_USER_GET_VRING_BASE``.
|
``VHOST_USER_GET_VRING_BASE``.
|
||||||
|
|
||||||
While processing the rings (whether they are enabled or not), client
|
While processing the rings (whether they are enabled or not), client
|
||||||
@ -767,25 +772,49 @@ When reconnecting:
|
|||||||
#. Resubmit inflight ``DescStatePacked`` entries in order of their
|
#. Resubmit inflight ``DescStatePacked`` entries in order of their
|
||||||
counter value
|
counter value
|
||||||
|
|
||||||
|
In-band notifications
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
In some limited situations (e.g. for simulation) it is desirable to
|
||||||
|
have the kick, call and error (if used) signals done via in-band
|
||||||
|
messages instead of asynchronous eventfd notifications. This can be
|
||||||
|
done by negotiating the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS``
|
||||||
|
protocol feature.
|
||||||
|
|
||||||
|
Note that due to the fact that too many messages on the sockets can
|
||||||
|
cause the sending application(s) to block, it is not advised to use
|
||||||
|
this feature unless absolutely necessary. It is also considered an
|
||||||
|
error to negotiate this feature without also negotiating
|
||||||
|
``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` and ``VHOST_USER_PROTOCOL_F_REPLY_ACK``,
|
||||||
|
the former is necessary for getting a message channel from the slave
|
||||||
|
to the master, while the latter needs to be used with the in-band
|
||||||
|
notification messages to block until they are processed, both to avoid
|
||||||
|
blocking later and for proper processing (at least in the simulation
|
||||||
|
use case.) As it has no other way of signalling this error, the slave
|
||||||
|
should close the connection as a response to a
|
||||||
|
``VHOST_USER_SET_PROTOCOL_FEATURES`` message that sets the in-band
|
||||||
|
notifications feature flag without the other two.
|
||||||
|
|
||||||
Protocol features
|
Protocol features
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
.. code:: c
|
.. code:: c
|
||||||
|
|
||||||
#define VHOST_USER_PROTOCOL_F_MQ 0
|
#define VHOST_USER_PROTOCOL_F_MQ 0
|
||||||
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
|
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
|
||||||
#define VHOST_USER_PROTOCOL_F_RARP 2
|
#define VHOST_USER_PROTOCOL_F_RARP 2
|
||||||
#define VHOST_USER_PROTOCOL_F_REPLY_ACK 3
|
#define VHOST_USER_PROTOCOL_F_REPLY_ACK 3
|
||||||
#define VHOST_USER_PROTOCOL_F_MTU 4
|
#define VHOST_USER_PROTOCOL_F_MTU 4
|
||||||
#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
|
#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
|
||||||
#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6
|
#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6
|
||||||
#define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7
|
#define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7
|
||||||
#define VHOST_USER_PROTOCOL_F_PAGEFAULT 8
|
#define VHOST_USER_PROTOCOL_F_PAGEFAULT 8
|
||||||
#define VHOST_USER_PROTOCOL_F_CONFIG 9
|
#define VHOST_USER_PROTOCOL_F_CONFIG 9
|
||||||
#define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10
|
#define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10
|
||||||
#define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11
|
#define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11
|
||||||
#define VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD 12
|
#define VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD 12
|
||||||
#define VHOST_USER_PROTOCOL_F_RESET_DEVICE 13
|
#define VHOST_USER_PROTOCOL_F_RESET_DEVICE 13
|
||||||
|
#define VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS 14
|
||||||
|
|
||||||
Master message types
|
Master message types
|
||||||
--------------------
|
--------------------
|
||||||
@ -947,7 +976,12 @@ Master message types
|
|||||||
Bits (0-7) of the payload contain the vring index. Bit 8 is the
|
Bits (0-7) of the payload contain the vring index. Bit 8 is the
|
||||||
invalid FD flag. This flag is set when there is no file descriptor
|
invalid FD flag. This flag is set when there is no file descriptor
|
||||||
in the ancillary data. This signals that polling should be used
|
in the ancillary data. This signals that polling should be used
|
||||||
instead of waiting for a kick.
|
instead of waiting for the kick. Note that if the protocol feature
|
||||||
|
``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` has been negotiated
|
||||||
|
this message isn't necessary as the ring is also started on the
|
||||||
|
``VHOST_USER_VRING_KICK`` message, it may however still be used to
|
||||||
|
set an event file descriptor (which will be preferred over the
|
||||||
|
message) or to enable polling.
|
||||||
|
|
||||||
``VHOST_USER_SET_VRING_CALL``
|
``VHOST_USER_SET_VRING_CALL``
|
||||||
:id: 13
|
:id: 13
|
||||||
@ -960,7 +994,12 @@ Master message types
|
|||||||
Bits (0-7) of the payload contain the vring index. Bit 8 is the
|
Bits (0-7) of the payload contain the vring index. Bit 8 is the
|
||||||
invalid FD flag. This flag is set when there is no file descriptor
|
invalid FD flag. This flag is set when there is no file descriptor
|
||||||
in the ancillary data. This signals that polling will be used
|
in the ancillary data. This signals that polling will be used
|
||||||
instead of waiting for the call.
|
instead of waiting for the call. Note that if the protocol features
|
||||||
|
``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` and
|
||||||
|
``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` have been negotiated this message
|
||||||
|
isn't necessary as the ``VHOST_USER_SLAVE_VRING_CALL`` message can be
|
||||||
|
used, it may however still be used to set an event file descriptor
|
||||||
|
or to enable polling.
|
||||||
|
|
||||||
``VHOST_USER_SET_VRING_ERR``
|
``VHOST_USER_SET_VRING_ERR``
|
||||||
:id: 14
|
:id: 14
|
||||||
@ -972,7 +1011,12 @@ Master message types
|
|||||||
|
|
||||||
Bits (0-7) of the payload contain the vring index. Bit 8 is the
|
Bits (0-7) of the payload contain the vring index. Bit 8 is the
|
||||||
invalid FD flag. This flag is set when there is no file descriptor
|
invalid FD flag. This flag is set when there is no file descriptor
|
||||||
in the ancillary data.
|
in the ancillary data. Note that if the protocol features
|
||||||
|
``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` and
|
||||||
|
``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` have been negotiated this message
|
||||||
|
isn't necessary as the ``VHOST_USER_SLAVE_VRING_ERR`` message can be
|
||||||
|
used, it may however still be used to set an event file descriptor
|
||||||
|
(which will be preferred over the message).
|
||||||
|
|
||||||
``VHOST_USER_GET_QUEUE_NUM``
|
``VHOST_USER_GET_QUEUE_NUM``
|
||||||
:id: 17
|
:id: 17
|
||||||
@ -1205,6 +1249,20 @@ Master message types
|
|||||||
Only valid if the ``VHOST_USER_PROTOCOL_F_RESET_DEVICE`` protocol
|
Only valid if the ``VHOST_USER_PROTOCOL_F_RESET_DEVICE`` protocol
|
||||||
feature is set by the backend.
|
feature is set by the backend.
|
||||||
|
|
||||||
|
``VHOST_USER_VRING_KICK``
|
||||||
|
:id: 35
|
||||||
|
:equivalent ioctl: N/A
|
||||||
|
:slave payload: vring state description
|
||||||
|
:master payload: N/A
|
||||||
|
|
||||||
|
When the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` protocol
|
||||||
|
feature has been successfully negotiated, this message may be
|
||||||
|
submitted by the master to indicate that a buffer was added to
|
||||||
|
the vring instead of signalling it using the vring's kick file
|
||||||
|
descriptor or having the slave rely on polling.
|
||||||
|
|
||||||
|
The state.num field is currently reserved and must be set to 0.
|
||||||
|
|
||||||
Slave message types
|
Slave message types
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
@ -1261,6 +1319,34 @@ Slave message types
|
|||||||
``VHOST_USER_PROTOCOL_F_HOST_NOTIFIER`` protocol feature has been
|
``VHOST_USER_PROTOCOL_F_HOST_NOTIFIER`` protocol feature has been
|
||||||
successfully negotiated.
|
successfully negotiated.
|
||||||
|
|
||||||
|
``VHOST_USER_SLAVE_VRING_CALL``
|
||||||
|
:id: 4
|
||||||
|
:equivalent ioctl: N/A
|
||||||
|
:slave payload: vring state description
|
||||||
|
:master payload: N/A
|
||||||
|
|
||||||
|
When the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` protocol
|
||||||
|
feature has been successfully negotiated, this message may be
|
||||||
|
submitted by the slave to indicate that a buffer was used from
|
||||||
|
the vring instead of signalling this using the vring's call file
|
||||||
|
descriptor or having the master relying on polling.
|
||||||
|
|
||||||
|
The state.num field is currently reserved and must be set to 0.
|
||||||
|
|
||||||
|
``VHOST_USER_SLAVE_VRING_ERR``
|
||||||
|
:id: 5
|
||||||
|
:equivalent ioctl: N/A
|
||||||
|
:slave payload: vring state description
|
||||||
|
:master payload: N/A
|
||||||
|
|
||||||
|
When the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` protocol
|
||||||
|
feature has been successfully negotiated, this message may be
|
||||||
|
submitted by the slave to indicate that an error occurred on the
|
||||||
|
specific vring, instead of signalling the error file descriptor
|
||||||
|
set by the master via ``VHOST_USER_SET_VRING_ERR``.
|
||||||
|
|
||||||
|
The state.num field is currently reserved and must be set to 0.
|
||||||
|
|
||||||
.. _reply_ack:
|
.. _reply_ack:
|
||||||
|
|
||||||
VHOST_USER_PROTOCOL_F_REPLY_ACK
|
VHOST_USER_PROTOCOL_F_REPLY_ACK
|
||||||
|
@ -94,6 +94,8 @@ write access:
|
|||||||
register in QEMU
|
register in QEMU
|
||||||
2: following writes to 'Command data' register set OST status
|
2: following writes to 'Command data' register set OST status
|
||||||
register in QEMU
|
register in QEMU
|
||||||
|
3: following reads from 'Command data' and 'Command data 2' return
|
||||||
|
architecture specific CPU ID value for currently selected CPU.
|
||||||
other values: reserved
|
other values: reserved
|
||||||
[0x6-0x7] reserved
|
[0x6-0x7] reserved
|
||||||
[0x8] Command data: (DWORD access)
|
[0x8] Command data: (DWORD access)
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/units.h"
|
#include "qemu/units.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
|
#include "monitor/qdev.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
@ -54,6 +55,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "hw/pci-host/gpex.h"
|
#include "hw/pci-host/gpex.h"
|
||||||
|
#include "hw/virtio/virtio-pci.h"
|
||||||
#include "hw/arm/sysbus-fdt.h"
|
#include "hw/arm/sysbus-fdt.h"
|
||||||
#include "hw/platform-bus.h"
|
#include "hw/platform-bus.h"
|
||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
@ -71,6 +73,7 @@
|
|||||||
#include "hw/mem/pc-dimm.h"
|
#include "hw/mem/pc-dimm.h"
|
||||||
#include "hw/mem/nvdimm.h"
|
#include "hw/mem/nvdimm.h"
|
||||||
#include "hw/acpi/generic_event_device.h"
|
#include "hw/acpi/generic_event_device.h"
|
||||||
|
#include "hw/virtio/virtio-iommu.h"
|
||||||
|
|
||||||
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
|
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
|
||||||
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
|
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
|
||||||
@ -1180,6 +1183,30 @@ static void create_smmu(const VirtMachineState *vms,
|
|||||||
g_free(node);
|
g_free(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_virtio_iommu_dt_bindings(VirtMachineState *vms, Error **errp)
|
||||||
|
{
|
||||||
|
const char compat[] = "virtio,pci-iommu";
|
||||||
|
uint16_t bdf = vms->virtio_iommu_bdf;
|
||||||
|
char *node;
|
||||||
|
|
||||||
|
vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt);
|
||||||
|
|
||||||
|
node = g_strdup_printf("%s/virtio_iommu@%d", vms->pciehb_nodename, bdf);
|
||||||
|
qemu_fdt_add_subnode(vms->fdt, node);
|
||||||
|
qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat));
|
||||||
|
qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg",
|
||||||
|
1, bdf << 8, 1, 0, 1, 0,
|
||||||
|
1, 0, 1, 0);
|
||||||
|
|
||||||
|
qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1);
|
||||||
|
qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle);
|
||||||
|
g_free(node);
|
||||||
|
|
||||||
|
qemu_fdt_setprop_cells(vms->fdt, vms->pciehb_nodename, "iommu-map",
|
||||||
|
0x0, vms->iommu_phandle, 0x0, bdf,
|
||||||
|
bdf + 1, vms->iommu_phandle, bdf + 1, 0xffff - bdf);
|
||||||
|
}
|
||||||
|
|
||||||
static void create_pcie(VirtMachineState *vms)
|
static void create_pcie(VirtMachineState *vms)
|
||||||
{
|
{
|
||||||
hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
|
hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
|
||||||
@ -1258,7 +1285,7 @@ static void create_pcie(VirtMachineState *vms)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodename = g_strdup_printf("/pcie@%" PRIx64, base);
|
nodename = vms->pciehb_nodename = g_strdup_printf("/pcie@%" PRIx64, base);
|
||||||
qemu_fdt_add_subnode(vms->fdt, nodename);
|
qemu_fdt_add_subnode(vms->fdt, nodename);
|
||||||
qemu_fdt_setprop_string(vms->fdt, nodename,
|
qemu_fdt_setprop_string(vms->fdt, nodename,
|
||||||
"compatible", "pci-host-ecam-generic");
|
"compatible", "pci-host-ecam-generic");
|
||||||
@ -1301,13 +1328,16 @@ static void create_pcie(VirtMachineState *vms)
|
|||||||
if (vms->iommu) {
|
if (vms->iommu) {
|
||||||
vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt);
|
vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt);
|
||||||
|
|
||||||
create_smmu(vms, pci->bus);
|
switch (vms->iommu) {
|
||||||
|
case VIRT_IOMMU_SMMUV3:
|
||||||
qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map",
|
create_smmu(vms, pci->bus);
|
||||||
0x0, vms->iommu_phandle, 0x0, 0x10000);
|
qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map",
|
||||||
|
0x0, vms->iommu_phandle, 0x0, 0x10000);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(nodename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_platform_bus(VirtMachineState *vms)
|
static void create_platform_bus(VirtMachineState *vms)
|
||||||
@ -1974,6 +2004,13 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
|
|||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||||
virt_memory_plug(hotplug_dev, dev, errp);
|
virt_memory_plug(hotplug_dev, dev, errp);
|
||||||
}
|
}
|
||||||
|
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
|
||||||
|
PCIDevice *pdev = PCI_DEVICE(dev);
|
||||||
|
|
||||||
|
vms->iommu = VIRT_IOMMU_VIRTIO;
|
||||||
|
vms->virtio_iommu_bdf = pci_get_bdf(pdev);
|
||||||
|
create_virtio_iommu_dt_bindings(vms, errp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
|
static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
|
||||||
@ -1990,7 +2027,13 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
|
|||||||
(object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
|
(object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
|
||||||
return HOTPLUG_HANDLER(machine);
|
return HOTPLUG_HANDLER(machine);
|
||||||
}
|
}
|
||||||
|
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
|
||||||
|
VirtMachineState *vms = VIRT_MACHINE(machine);
|
||||||
|
|
||||||
|
if (!vms->bootinfo.firmware_loaded || !acpi_enabled) {
|
||||||
|
return HOTPLUG_HANDLER(machine);
|
||||||
|
}
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ static int vhost_user_blk_connect(DeviceState *dev)
|
|||||||
s->connected = true;
|
s->connected = true;
|
||||||
|
|
||||||
s->dev.nvqs = s->num_queues;
|
s->dev.nvqs = s->num_queues;
|
||||||
s->dev.vqs = s->vqs;
|
s->dev.vqs = s->vhost_vqs;
|
||||||
s->dev.vq_index = 0;
|
s->dev.vq_index = 0;
|
||||||
s->dev.backend_features = 0;
|
s->dev.backend_features = 0;
|
||||||
|
|
||||||
@ -420,13 +420,14 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
|
|||||||
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
|
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
|
||||||
sizeof(struct virtio_blk_config));
|
sizeof(struct virtio_blk_config));
|
||||||
|
|
||||||
|
s->virtqs = g_new(VirtQueue *, s->num_queues);
|
||||||
for (i = 0; i < s->num_queues; i++) {
|
for (i = 0; i < s->num_queues; i++) {
|
||||||
virtio_add_queue(vdev, s->queue_size,
|
s->virtqs[i] = virtio_add_queue(vdev, s->queue_size,
|
||||||
vhost_user_blk_handle_output);
|
vhost_user_blk_handle_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
s->inflight = g_new0(struct vhost_inflight, 1);
|
s->inflight = g_new0(struct vhost_inflight, 1);
|
||||||
s->vqs = g_new0(struct vhost_virtqueue, s->num_queues);
|
s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues);
|
||||||
s->watch = 0;
|
s->watch = 0;
|
||||||
s->connected = false;
|
s->connected = false;
|
||||||
|
|
||||||
@ -458,8 +459,12 @@ reconnect:
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
virtio_err:
|
virtio_err:
|
||||||
g_free(s->vqs);
|
g_free(s->vhost_vqs);
|
||||||
g_free(s->inflight);
|
g_free(s->inflight);
|
||||||
|
for (i = 0; i < s->num_queues; i++) {
|
||||||
|
virtio_delete_queue(s->virtqs[i]);
|
||||||
|
}
|
||||||
|
g_free(s->virtqs);
|
||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
vhost_user_cleanup(&s->vhost_user);
|
vhost_user_cleanup(&s->vhost_user);
|
||||||
}
|
}
|
||||||
@ -468,14 +473,20 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
|
|||||||
{
|
{
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
VHostUserBlk *s = VHOST_USER_BLK(dev);
|
VHostUserBlk *s = VHOST_USER_BLK(dev);
|
||||||
|
int i;
|
||||||
|
|
||||||
virtio_set_status(vdev, 0);
|
virtio_set_status(vdev, 0);
|
||||||
qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL,
|
qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL,
|
||||||
NULL, NULL, NULL, false);
|
NULL, NULL, NULL, false);
|
||||||
vhost_dev_cleanup(&s->dev);
|
vhost_dev_cleanup(&s->dev);
|
||||||
vhost_dev_free_inflight(s->inflight);
|
vhost_dev_free_inflight(s->inflight);
|
||||||
g_free(s->vqs);
|
g_free(s->vhost_vqs);
|
||||||
g_free(s->inflight);
|
g_free(s->inflight);
|
||||||
|
|
||||||
|
for (i = 0; i < s->num_queues; i++) {
|
||||||
|
virtio_delete_queue(s->virtqs[i]);
|
||||||
|
}
|
||||||
|
g_free(s->virtqs);
|
||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
vhost_user_cleanup(&s->vhost_user);
|
vhost_user_cleanup(&s->vhost_user);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,11 @@ config VIRTIO_RNG
|
|||||||
default y
|
default y
|
||||||
depends on VIRTIO
|
depends on VIRTIO
|
||||||
|
|
||||||
|
config VIRTIO_IOMMU
|
||||||
|
bool
|
||||||
|
default y
|
||||||
|
depends on VIRTIO
|
||||||
|
|
||||||
config VIRTIO_PCI
|
config VIRTIO_PCI
|
||||||
bool
|
bool
|
||||||
default y if PCI_DEVICES
|
default y if PCI_DEVICES
|
||||||
|
@ -16,6 +16,7 @@ obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-p
|
|||||||
obj-$(CONFIG_VIRTIO_PMEM) += virtio-pmem.o
|
obj-$(CONFIG_VIRTIO_PMEM) += virtio-pmem.o
|
||||||
common-obj-$(call land,$(CONFIG_VIRTIO_PMEM),$(CONFIG_VIRTIO_PCI)) += virtio-pmem-pci.o
|
common-obj-$(call land,$(CONFIG_VIRTIO_PMEM),$(CONFIG_VIRTIO_PCI)) += virtio-pmem-pci.o
|
||||||
obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-pci.o
|
obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-pci.o
|
||||||
|
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
|
||||||
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
|
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_VIRTIO_PCI),y)
|
ifeq ($(CONFIG_VIRTIO_PCI),y)
|
||||||
@ -28,6 +29,7 @@ obj-$(CONFIG_VIRTIO_INPUT_HOST) += virtio-input-host-pci.o
|
|||||||
obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-pci.o
|
obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-pci.o
|
||||||
obj-$(CONFIG_VIRTIO_RNG) += virtio-rng-pci.o
|
obj-$(CONFIG_VIRTIO_RNG) += virtio-rng-pci.o
|
||||||
obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon-pci.o
|
obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon-pci.o
|
||||||
|
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu-pci.o
|
||||||
obj-$(CONFIG_VIRTIO_9P) += virtio-9p-pci.o
|
obj-$(CONFIG_VIRTIO_9P) += virtio-9p-pci.o
|
||||||
obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi-pci.o
|
obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi-pci.o
|
||||||
obj-$(CONFIG_VIRTIO_BLK) += virtio-blk-pci.o
|
obj-$(CONFIG_VIRTIO_BLK) += virtio-blk-pci.o
|
||||||
|
@ -53,3 +53,23 @@ virtio_mmio_write_offset(uint64_t offset, uint64_t value) "virtio_mmio_write off
|
|||||||
virtio_mmio_guest_page(uint64_t size, int shift) "guest page size 0x%" PRIx64 " shift %d"
|
virtio_mmio_guest_page(uint64_t size, int shift) "guest page size 0x%" PRIx64 " shift %d"
|
||||||
virtio_mmio_queue_write(uint64_t value, int max_size) "mmio_queue write 0x%" PRIx64 " max %d"
|
virtio_mmio_queue_write(uint64_t value, int max_size) "mmio_queue write 0x%" PRIx64 " max %d"
|
||||||
virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d"
|
virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d"
|
||||||
|
|
||||||
|
# hw/virtio/virtio-iommu.c
|
||||||
|
virtio_iommu_device_reset(void) "reset!"
|
||||||
|
virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64
|
||||||
|
virtio_iommu_device_status(uint8_t status) "driver status = %d"
|
||||||
|
virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_range, uint32_t probe_size) "page_size_mask=0x%"PRIx64" start=0x%"PRIx64" end=0x%"PRIx64" domain_range=%d probe_size=0x%x"
|
||||||
|
virtio_iommu_set_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_range, uint32_t probe_size) "page_size_mask=0x%"PRIx64" start=0x%"PRIx64" end=0x%"PRIx64" domain_bits=%d probe_size=0x%x"
|
||||||
|
virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
||||||
|
virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
||||||
|
virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d"
|
||||||
|
virtio_iommu_unmap(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64
|
||||||
|
virtio_iommu_unmap_done(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64
|
||||||
|
virtio_iommu_translate(const char *name, uint32_t rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64" flag=%d"
|
||||||
|
virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s"
|
||||||
|
virtio_iommu_get_endpoint(uint32_t ep_id) "Alloc endpoint=%d"
|
||||||
|
virtio_iommu_put_endpoint(uint32_t ep_id) "Free endpoint=%d"
|
||||||
|
virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d"
|
||||||
|
virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d"
|
||||||
|
virtio_iommu_translate_out(uint64_t virt_addr, uint64_t phys_addr, uint32_t sid) "0x%"PRIx64" -> 0x%"PRIx64 " for sid=%d"
|
||||||
|
virtio_iommu_report_fault(uint8_t reason, uint32_t flags, uint32_t endpoint, uint64_t addr) "FAULT reason=%d flags=%d endpoint=%d address =0x%"PRIx64
|
||||||
|
@ -209,11 +209,12 @@ static void vuf_device_realize(DeviceState *dev, Error **errp)
|
|||||||
sizeof(struct virtio_fs_config));
|
sizeof(struct virtio_fs_config));
|
||||||
|
|
||||||
/* Hiprio queue */
|
/* Hiprio queue */
|
||||||
virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
|
fs->hiprio_vq = virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
|
||||||
|
|
||||||
/* Request queues */
|
/* Request queues */
|
||||||
|
fs->req_vqs = g_new(VirtQueue *, fs->conf.num_request_queues);
|
||||||
for (i = 0; i < fs->conf.num_request_queues; i++) {
|
for (i = 0; i < fs->conf.num_request_queues; i++) {
|
||||||
virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
|
fs->req_vqs[i] = virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1 high prio queue, plus the number configured */
|
/* 1 high prio queue, plus the number configured */
|
||||||
@ -230,6 +231,11 @@ static void vuf_device_realize(DeviceState *dev, Error **errp)
|
|||||||
|
|
||||||
err_virtio:
|
err_virtio:
|
||||||
vhost_user_cleanup(&fs->vhost_user);
|
vhost_user_cleanup(&fs->vhost_user);
|
||||||
|
virtio_delete_queue(fs->hiprio_vq);
|
||||||
|
for (i = 0; i < fs->conf.num_request_queues; i++) {
|
||||||
|
virtio_delete_queue(fs->req_vqs[i]);
|
||||||
|
}
|
||||||
|
g_free(fs->req_vqs);
|
||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
g_free(fs->vhost_dev.vqs);
|
g_free(fs->vhost_dev.vqs);
|
||||||
return;
|
return;
|
||||||
@ -239,6 +245,7 @@ static void vuf_device_unrealize(DeviceState *dev, Error **errp)
|
|||||||
{
|
{
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
VHostUserFS *fs = VHOST_USER_FS(dev);
|
VHostUserFS *fs = VHOST_USER_FS(dev);
|
||||||
|
int i;
|
||||||
|
|
||||||
/* This will stop vhost backend if appropriate. */
|
/* This will stop vhost backend if appropriate. */
|
||||||
vuf_set_status(vdev, 0);
|
vuf_set_status(vdev, 0);
|
||||||
@ -247,6 +254,11 @@ static void vuf_device_unrealize(DeviceState *dev, Error **errp)
|
|||||||
|
|
||||||
vhost_user_cleanup(&fs->vhost_user);
|
vhost_user_cleanup(&fs->vhost_user);
|
||||||
|
|
||||||
|
virtio_delete_queue(fs->hiprio_vq);
|
||||||
|
for (i = 0; i < fs->conf.num_request_queues; i++) {
|
||||||
|
virtio_delete_queue(fs->req_vqs[i]);
|
||||||
|
}
|
||||||
|
g_free(fs->req_vqs);
|
||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
g_free(fs->vhost_dev.vqs);
|
g_free(fs->vhost_dev.vqs);
|
||||||
fs->vhost_dev.vqs = NULL;
|
fs->vhost_dev.vqs = NULL;
|
||||||
|
@ -443,6 +443,7 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev,
|
|||||||
&offset);
|
&offset);
|
||||||
fd = memory_region_get_fd(mr);
|
fd = memory_region_get_fd(mr);
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
|
assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
|
||||||
trace_vhost_user_set_mem_table_withfd(fd_num, mr->name,
|
trace_vhost_user_set_mem_table_withfd(fd_num, mr->name,
|
||||||
reg->memory_size,
|
reg->memory_size,
|
||||||
reg->guest_phys_addr,
|
reg->guest_phys_addr,
|
||||||
@ -455,7 +456,6 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev,
|
|||||||
msg.payload.memory.regions[fd_num].guest_phys_addr =
|
msg.payload.memory.regions[fd_num].guest_phys_addr =
|
||||||
reg->guest_phys_addr;
|
reg->guest_phys_addr;
|
||||||
msg.payload.memory.regions[fd_num].mmap_offset = offset;
|
msg.payload.memory.regions[fd_num].mmap_offset = offset;
|
||||||
assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
|
|
||||||
fds[fd_num++] = fd;
|
fds[fd_num++] = fd;
|
||||||
} else {
|
} else {
|
||||||
u->region_rb_offset[i] = 0;
|
u->region_rb_offset[i] = 0;
|
||||||
@ -1458,9 +1458,11 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
|
|||||||
"VHOST_USER_PROTOCOL_F_LOG_SHMFD feature.");
|
"VHOST_USER_PROTOCOL_F_LOG_SHMFD feature.");
|
||||||
}
|
}
|
||||||
|
|
||||||
err = vhost_setup_slave_channel(dev);
|
if (dev->vq_index == 0) {
|
||||||
if (err < 0) {
|
err = vhost_setup_slave_channel(dev);
|
||||||
return err;
|
if (err < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u->postcopy_notifier.notify = vhost_user_postcopy_notifier;
|
u->postcopy_notifier.notify = vhost_user_postcopy_notifier;
|
||||||
|
@ -831,12 +831,13 @@ static void virtio_crypto_device_unrealize(DeviceState *dev, Error **errp)
|
|||||||
|
|
||||||
max_queues = vcrypto->multiqueue ? vcrypto->max_queues : 1;
|
max_queues = vcrypto->multiqueue ? vcrypto->max_queues : 1;
|
||||||
for (i = 0; i < max_queues; i++) {
|
for (i = 0; i < max_queues; i++) {
|
||||||
virtio_del_queue(vdev, i);
|
virtio_delete_queue(vcrypto->vqs[i].dataq);
|
||||||
q = &vcrypto->vqs[i];
|
q = &vcrypto->vqs[i];
|
||||||
qemu_bh_delete(q->dataq_bh);
|
qemu_bh_delete(q->dataq_bh);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(vcrypto->vqs);
|
g_free(vcrypto->vqs);
|
||||||
|
virtio_delete_queue(vcrypto->ctrl_vq);
|
||||||
|
|
||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
cryptodev_backend_set_used(vcrypto->cryptodev, false);
|
cryptodev_backend_set_used(vcrypto->cryptodev, false);
|
||||||
|
104
hw/virtio/virtio-iommu-pci.c
Normal file
104
hw/virtio/virtio-iommu-pci.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Virtio IOMMU PCI Bindings
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Red Hat, Inc.
|
||||||
|
* Written by Eric Auger
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "virtio-pci.h"
|
||||||
|
#include "hw/virtio/virtio-iommu.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "hw/boards.h"
|
||||||
|
|
||||||
|
typedef struct VirtIOIOMMUPCI VirtIOIOMMUPCI;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* virtio-iommu-pci: This extends VirtioPCIProxy.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define VIRTIO_IOMMU_PCI(obj) \
|
||||||
|
OBJECT_CHECK(VirtIOIOMMUPCI, (obj), TYPE_VIRTIO_IOMMU_PCI)
|
||||||
|
|
||||||
|
struct VirtIOIOMMUPCI {
|
||||||
|
VirtIOPCIProxy parent_obj;
|
||||||
|
VirtIOIOMMU vdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Property virtio_iommu_pci_properties[] = {
|
||||||
|
DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(vpci_dev);
|
||||||
|
DeviceState *vdev = DEVICE(&dev->vdev);
|
||||||
|
|
||||||
|
if (!qdev_get_machine_hotplug_handler(DEVICE(vpci_dev))) {
|
||||||
|
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||||
|
|
||||||
|
error_setg(errp,
|
||||||
|
"%s machine fails to create iommu-map device tree bindings",
|
||||||
|
mc->name);
|
||||||
|
error_append_hint(errp,
|
||||||
|
"Check you machine implements a hotplug handler "
|
||||||
|
"for the virtio-iommu-pci device\n");
|
||||||
|
error_append_hint(errp, "Check the guest is booted without FW or with "
|
||||||
|
"-no-acpi\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
|
||||||
|
object_property_set_link(OBJECT(dev),
|
||||||
|
OBJECT(pci_get_bus(&vpci_dev->pci_dev)),
|
||||||
|
"primary-bus", errp);
|
||||||
|
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_pci_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
|
||||||
|
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
||||||
|
k->realize = virtio_iommu_pci_realize;
|
||||||
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||||
|
device_class_set_props(dc, virtio_iommu_pci_properties);
|
||||||
|
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
|
||||||
|
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_IOMMU;
|
||||||
|
pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
|
||||||
|
pcidev_k->class_id = PCI_CLASS_OTHERS;
|
||||||
|
dc->hotpluggable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_pci_instance_init(Object *obj)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(obj);
|
||||||
|
|
||||||
|
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||||
|
TYPE_VIRTIO_IOMMU);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = {
|
||||||
|
.base_name = TYPE_VIRTIO_IOMMU_PCI,
|
||||||
|
.generic_name = "virtio-iommu-pci",
|
||||||
|
.transitional_name = "virtio-iommu-pci-transitional",
|
||||||
|
.non_transitional_name = "virtio-iommu-pci-non-transitional",
|
||||||
|
.instance_size = sizeof(VirtIOIOMMUPCI),
|
||||||
|
.instance_init = virtio_iommu_pci_instance_init,
|
||||||
|
.class_init = virtio_iommu_pci_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void virtio_iommu_pci_register(void)
|
||||||
|
{
|
||||||
|
virtio_pci_types_register(&virtio_iommu_pci_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(virtio_iommu_pci_register)
|
||||||
|
|
||||||
|
|
890
hw/virtio/virtio-iommu.c
Normal file
890
hw/virtio/virtio-iommu.c
Normal file
@ -0,0 +1,890 @@
|
|||||||
|
/*
|
||||||
|
* 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/log.h"
|
||||||
|
#include "qemu/iov.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "hw/virtio/virtio.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu/error-report.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"
|
||||||
|
#include "hw/pci/pci_bus.h"
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
|
||||||
|
/* Max size */
|
||||||
|
#define VIOMMU_DEFAULT_QUEUE_SIZE 256
|
||||||
|
|
||||||
|
typedef struct VirtIOIOMMUDomain {
|
||||||
|
uint32_t id;
|
||||||
|
GTree *mappings;
|
||||||
|
QLIST_HEAD(, VirtIOIOMMUEndpoint) endpoint_list;
|
||||||
|
} VirtIOIOMMUDomain;
|
||||||
|
|
||||||
|
typedef struct VirtIOIOMMUEndpoint {
|
||||||
|
uint32_t id;
|
||||||
|
VirtIOIOMMUDomain *domain;
|
||||||
|
QLIST_ENTRY(VirtIOIOMMUEndpoint) next;
|
||||||
|
} VirtIOIOMMUEndpoint;
|
||||||
|
|
||||||
|
typedef struct VirtIOIOMMUInterval {
|
||||||
|
uint64_t low;
|
||||||
|
uint64_t high;
|
||||||
|
} VirtIOIOMMUInterval;
|
||||||
|
|
||||||
|
typedef struct VirtIOIOMMUMapping {
|
||||||
|
uint64_t phys_addr;
|
||||||
|
uint32_t flags;
|
||||||
|
} VirtIOIOMMUMapping;
|
||||||
|
|
||||||
|
static inline uint16_t virtio_iommu_get_bdf(IOMMUDevice *dev)
|
||||||
|
{
|
||||||
|
return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bus number is used for lookup when SID based operations occur.
|
||||||
|
* In that case we lazily populate the IOMMUPciBus array from the bus hash
|
||||||
|
* table. At the time the IOMMUPciBus is created (iommu_find_add_as), the bus
|
||||||
|
* numbers may not be always initialized yet.
|
||||||
|
*/
|
||||||
|
static IOMMUPciBus *iommu_find_iommu_pcibus(VirtIOIOMMU *s, uint8_t bus_num)
|
||||||
|
{
|
||||||
|
IOMMUPciBus *iommu_pci_bus = s->iommu_pcibus_by_bus_num[bus_num];
|
||||||
|
|
||||||
|
if (!iommu_pci_bus) {
|
||||||
|
GHashTableIter iter;
|
||||||
|
|
||||||
|
g_hash_table_iter_init(&iter, s->as_by_busptr);
|
||||||
|
while (g_hash_table_iter_next(&iter, NULL, (void **)&iommu_pci_bus)) {
|
||||||
|
if (pci_bus_num(iommu_pci_bus->bus) == bus_num) {
|
||||||
|
s->iommu_pcibus_by_bus_num[bus_num] = iommu_pci_bus;
|
||||||
|
return iommu_pci_bus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return iommu_pci_bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IOMMUMemoryRegion *virtio_iommu_mr(VirtIOIOMMU *s, uint32_t sid)
|
||||||
|
{
|
||||||
|
uint8_t bus_n, devfn;
|
||||||
|
IOMMUPciBus *iommu_pci_bus;
|
||||||
|
IOMMUDevice *dev;
|
||||||
|
|
||||||
|
bus_n = PCI_BUS_NUM(sid);
|
||||||
|
iommu_pci_bus = iommu_find_iommu_pcibus(s, bus_n);
|
||||||
|
if (iommu_pci_bus) {
|
||||||
|
devfn = sid & PCI_DEVFN_MAX;
|
||||||
|
dev = iommu_pci_bus->pbdev[devfn];
|
||||||
|
if (dev) {
|
||||||
|
return &dev->iommu_mr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUInterval *inta = (VirtIOIOMMUInterval *)a;
|
||||||
|
VirtIOIOMMUInterval *intb = (VirtIOIOMMUInterval *)b;
|
||||||
|
|
||||||
|
if (inta->high < intb->low) {
|
||||||
|
return -1;
|
||||||
|
} else if (intb->high < inta->low) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep)
|
||||||
|
{
|
||||||
|
if (!ep->domain) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QLIST_REMOVE(ep, next);
|
||||||
|
ep->domain = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s,
|
||||||
|
uint32_t ep_id)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUEndpoint *ep;
|
||||||
|
|
||||||
|
ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id));
|
||||||
|
if (ep) {
|
||||||
|
return ep;
|
||||||
|
}
|
||||||
|
if (!virtio_iommu_mr(s, ep_id)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ep = g_malloc0(sizeof(*ep));
|
||||||
|
ep->id = ep_id;
|
||||||
|
trace_virtio_iommu_get_endpoint(ep_id);
|
||||||
|
g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep);
|
||||||
|
return ep;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_put_endpoint(gpointer data)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUEndpoint *ep = (VirtIOIOMMUEndpoint *)data;
|
||||||
|
|
||||||
|
if (ep->domain) {
|
||||||
|
virtio_iommu_detach_endpoint_from_domain(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_virtio_iommu_put_endpoint(ep->id);
|
||||||
|
g_free(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s,
|
||||||
|
uint32_t domain_id)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUDomain *domain;
|
||||||
|
|
||||||
|
domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
|
||||||
|
if (domain) {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
domain = g_malloc0(sizeof(*domain));
|
||||||
|
domain->id = domain_id;
|
||||||
|
domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
|
||||||
|
NULL, (GDestroyNotify)g_free,
|
||||||
|
(GDestroyNotify)g_free);
|
||||||
|
g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain);
|
||||||
|
QLIST_INIT(&domain->endpoint_list);
|
||||||
|
trace_virtio_iommu_get_domain(domain_id);
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_put_domain(gpointer data)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUDomain *domain = (VirtIOIOMMUDomain *)data;
|
||||||
|
VirtIOIOMMUEndpoint *iter, *tmp;
|
||||||
|
|
||||||
|
QLIST_FOREACH_SAFE(iter, &domain->endpoint_list, next, tmp) {
|
||||||
|
virtio_iommu_detach_endpoint_from_domain(iter);
|
||||||
|
}
|
||||||
|
g_tree_destroy(domain->mappings);
|
||||||
|
trace_virtio_iommu_put_domain(domain->id);
|
||||||
|
g_free(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
|
||||||
|
int devfn)
|
||||||
|
{
|
||||||
|
VirtIOIOMMU *s = opaque;
|
||||||
|
IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
|
||||||
|
static uint32_t mr_index;
|
||||||
|
IOMMUDevice *sdev;
|
||||||
|
|
||||||
|
if (!sbus) {
|
||||||
|
sbus = g_malloc0(sizeof(IOMMUPciBus) +
|
||||||
|
sizeof(IOMMUDevice *) * PCI_DEVFN_MAX);
|
||||||
|
sbus->bus = bus;
|
||||||
|
g_hash_table_insert(s->as_by_busptr, bus, sbus);
|
||||||
|
}
|
||||||
|
|
||||||
|
sdev = sbus->pbdev[devfn];
|
||||||
|
if (!sdev) {
|
||||||
|
char *name = g_strdup_printf("%s-%d-%d",
|
||||||
|
TYPE_VIRTIO_IOMMU_MEMORY_REGION,
|
||||||
|
mr_index++, devfn);
|
||||||
|
sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice));
|
||||||
|
|
||||||
|
sdev->viommu = s;
|
||||||
|
sdev->bus = bus;
|
||||||
|
sdev->devfn = devfn;
|
||||||
|
|
||||||
|
trace_virtio_iommu_init_iommu_mr(name);
|
||||||
|
|
||||||
|
memory_region_init_iommu(&sdev->iommu_mr, sizeof(sdev->iommu_mr),
|
||||||
|
TYPE_VIRTIO_IOMMU_MEMORY_REGION,
|
||||||
|
OBJECT(s), name,
|
||||||
|
UINT64_MAX);
|
||||||
|
address_space_init(&sdev->as,
|
||||||
|
MEMORY_REGION(&sdev->iommu_mr), TYPE_VIRTIO_IOMMU);
|
||||||
|
g_free(name);
|
||||||
|
}
|
||||||
|
return &sdev->as;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
VirtIOIOMMUDomain *domain;
|
||||||
|
VirtIOIOMMUEndpoint *ep;
|
||||||
|
|
||||||
|
trace_virtio_iommu_attach(domain_id, ep_id);
|
||||||
|
|
||||||
|
ep = virtio_iommu_get_endpoint(s, ep_id);
|
||||||
|
if (!ep) {
|
||||||
|
return VIRTIO_IOMMU_S_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ep->domain) {
|
||||||
|
VirtIOIOMMUDomain *previous_domain = ep->domain;
|
||||||
|
/*
|
||||||
|
* the device is already attached to a domain,
|
||||||
|
* detach it first
|
||||||
|
*/
|
||||||
|
virtio_iommu_detach_endpoint_from_domain(ep);
|
||||||
|
if (QLIST_EMPTY(&previous_domain->endpoint_list)) {
|
||||||
|
g_tree_remove(s->domains, GUINT_TO_POINTER(previous_domain->id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = virtio_iommu_get_domain(s, domain_id);
|
||||||
|
QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next);
|
||||||
|
|
||||||
|
ep->domain = domain;
|
||||||
|
|
||||||
|
return VIRTIO_IOMMU_S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
VirtIOIOMMUDomain *domain;
|
||||||
|
VirtIOIOMMUEndpoint *ep;
|
||||||
|
|
||||||
|
trace_virtio_iommu_detach(domain_id, ep_id);
|
||||||
|
|
||||||
|
ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id));
|
||||||
|
if (!ep) {
|
||||||
|
return VIRTIO_IOMMU_S_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = ep->domain;
|
||||||
|
|
||||||
|
if (!domain || domain->id != domain_id) {
|
||||||
|
return VIRTIO_IOMMU_S_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtio_iommu_detach_endpoint_from_domain(ep);
|
||||||
|
|
||||||
|
if (QLIST_EMPTY(&domain->endpoint_list)) {
|
||||||
|
g_tree_remove(s->domains, GUINT_TO_POINTER(domain->id));
|
||||||
|
}
|
||||||
|
return VIRTIO_IOMMU_S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
VirtIOIOMMUDomain *domain;
|
||||||
|
VirtIOIOMMUInterval *interval;
|
||||||
|
VirtIOIOMMUMapping *mapping;
|
||||||
|
|
||||||
|
if (flags & ~VIRTIO_IOMMU_MAP_F_MASK) {
|
||||||
|
return VIRTIO_IOMMU_S_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
|
||||||
|
if (!domain) {
|
||||||
|
return VIRTIO_IOMMU_S_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = g_malloc0(sizeof(*interval));
|
||||||
|
|
||||||
|
interval->low = virt_start;
|
||||||
|
interval->high = virt_end;
|
||||||
|
|
||||||
|
mapping = g_tree_lookup(domain->mappings, (gpointer)interval);
|
||||||
|
if (mapping) {
|
||||||
|
g_free(interval);
|
||||||
|
return VIRTIO_IOMMU_S_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_virtio_iommu_map(domain_id, virt_start, virt_end, phys_start, flags);
|
||||||
|
|
||||||
|
mapping = g_malloc0(sizeof(*mapping));
|
||||||
|
mapping->phys_addr = phys_start;
|
||||||
|
mapping->flags = flags;
|
||||||
|
|
||||||
|
g_tree_insert(domain->mappings, interval, mapping);
|
||||||
|
|
||||||
|
return VIRTIO_IOMMU_S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
VirtIOIOMMUMapping *iter_val;
|
||||||
|
VirtIOIOMMUInterval interval, *iter_key;
|
||||||
|
VirtIOIOMMUDomain *domain;
|
||||||
|
int ret = VIRTIO_IOMMU_S_OK;
|
||||||
|
|
||||||
|
trace_virtio_iommu_unmap(domain_id, virt_start, virt_end);
|
||||||
|
|
||||||
|
domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
|
||||||
|
if (!domain) {
|
||||||
|
return VIRTIO_IOMMU_S_NOENT;
|
||||||
|
}
|
||||||
|
interval.low = virt_start;
|
||||||
|
interval.high = virt_end;
|
||||||
|
|
||||||
|
while (g_tree_lookup_extended(domain->mappings, &interval,
|
||||||
|
(void **)&iter_key, (void**)&iter_val)) {
|
||||||
|
uint64_t current_low = iter_key->low;
|
||||||
|
uint64_t current_high = iter_key->high;
|
||||||
|
|
||||||
|
if (interval.low <= current_low && interval.high >= current_high) {
|
||||||
|
g_tree_remove(domain->mappings, iter_key);
|
||||||
|
trace_virtio_iommu_unmap_done(domain_id, current_low, current_high);
|
||||||
|
} else {
|
||||||
|
ret = VIRTIO_IOMMU_S_RANGE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_report_fault(VirtIOIOMMU *viommu, uint8_t reason,
|
||||||
|
int flags, uint32_t endpoint,
|
||||||
|
uint64_t address)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = &viommu->parent_obj;
|
||||||
|
VirtQueue *vq = viommu->event_vq;
|
||||||
|
struct virtio_iommu_fault fault;
|
||||||
|
VirtQueueElement *elem;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
memset(&fault, 0, sizeof(fault));
|
||||||
|
fault.reason = reason;
|
||||||
|
fault.flags = cpu_to_le32(flags);
|
||||||
|
fault.endpoint = cpu_to_le32(endpoint);
|
||||||
|
fault.address = cpu_to_le64(address);
|
||||||
|
|
||||||
|
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
|
||||||
|
|
||||||
|
if (!elem) {
|
||||||
|
error_report_once(
|
||||||
|
"no buffer available in event queue to report event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iov_size(elem->in_sg, elem->in_num) < sizeof(fault)) {
|
||||||
|
virtio_error(vdev, "error buffer of wrong size");
|
||||||
|
virtqueue_detach_element(vq, elem, 0);
|
||||||
|
g_free(elem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sz = iov_from_buf(elem->in_sg, elem->in_num, 0,
|
||||||
|
&fault, sizeof(fault));
|
||||||
|
assert(sz == sizeof(fault));
|
||||||
|
|
||||||
|
trace_virtio_iommu_report_fault(reason, flags, endpoint, address);
|
||||||
|
virtqueue_push(vq, elem, sz);
|
||||||
|
virtio_notify(vdev, vq);
|
||||||
|
g_free(elem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr,
|
||||||
|
IOMMUAccessFlags flag,
|
||||||
|
int iommu_idx)
|
||||||
|
{
|
||||||
|
IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr);
|
||||||
|
VirtIOIOMMUInterval interval, *mapping_key;
|
||||||
|
VirtIOIOMMUMapping *mapping_value;
|
||||||
|
VirtIOIOMMU *s = sdev->viommu;
|
||||||
|
bool read_fault, write_fault;
|
||||||
|
VirtIOIOMMUEndpoint *ep;
|
||||||
|
uint32_t sid, flags;
|
||||||
|
bool bypass_allowed;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
interval.low = addr;
|
||||||
|
interval.high = addr + 1;
|
||||||
|
|
||||||
|
IOMMUTLBEntry entry = {
|
||||||
|
.target_as = &address_space_memory,
|
||||||
|
.iova = addr,
|
||||||
|
.translated_addr = addr,
|
||||||
|
.addr_mask = (1 << ctz32(s->config.page_size_mask)) - 1,
|
||||||
|
.perm = IOMMU_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
bypass_allowed = virtio_vdev_has_feature(&s->parent_obj,
|
||||||
|
VIRTIO_IOMMU_F_BYPASS);
|
||||||
|
|
||||||
|
sid = virtio_iommu_get_bdf(sdev);
|
||||||
|
|
||||||
|
trace_virtio_iommu_translate(mr->parent_obj.name, sid, addr, flag);
|
||||||
|
qemu_mutex_lock(&s->mutex);
|
||||||
|
|
||||||
|
ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(sid));
|
||||||
|
if (!ep) {
|
||||||
|
if (!bypass_allowed) {
|
||||||
|
error_report_once("%s sid=%d is not known!!", __func__, sid);
|
||||||
|
virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_UNKNOWN,
|
||||||
|
VIRTIO_IOMMU_FAULT_F_ADDRESS,
|
||||||
|
sid, addr);
|
||||||
|
} else {
|
||||||
|
entry.perm = flag;
|
||||||
|
}
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ep->domain) {
|
||||||
|
if (!bypass_allowed) {
|
||||||
|
error_report_once("%s %02x:%02x.%01x not attached to any domain",
|
||||||
|
__func__, PCI_BUS_NUM(sid),
|
||||||
|
PCI_SLOT(sid), PCI_FUNC(sid));
|
||||||
|
virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_DOMAIN,
|
||||||
|
VIRTIO_IOMMU_FAULT_F_ADDRESS,
|
||||||
|
sid, addr);
|
||||||
|
} else {
|
||||||
|
entry.perm = flag;
|
||||||
|
}
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = g_tree_lookup_extended(ep->domain->mappings, (gpointer)(&interval),
|
||||||
|
(void **)&mapping_key,
|
||||||
|
(void **)&mapping_value);
|
||||||
|
if (!found) {
|
||||||
|
error_report_once("%s no mapping for 0x%"PRIx64" for sid=%d",
|
||||||
|
__func__, addr, sid);
|
||||||
|
virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_MAPPING,
|
||||||
|
VIRTIO_IOMMU_FAULT_F_ADDRESS,
|
||||||
|
sid, addr);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_fault = (flag & IOMMU_RO) &&
|
||||||
|
!(mapping_value->flags & VIRTIO_IOMMU_MAP_F_READ);
|
||||||
|
write_fault = (flag & IOMMU_WO) &&
|
||||||
|
!(mapping_value->flags & VIRTIO_IOMMU_MAP_F_WRITE);
|
||||||
|
|
||||||
|
flags = read_fault ? VIRTIO_IOMMU_FAULT_F_READ : 0;
|
||||||
|
flags |= write_fault ? VIRTIO_IOMMU_FAULT_F_WRITE : 0;
|
||||||
|
if (flags) {
|
||||||
|
error_report_once("%s permission error on 0x%"PRIx64"(%d): allowed=%d",
|
||||||
|
__func__, addr, flag, mapping_value->flags);
|
||||||
|
flags |= VIRTIO_IOMMU_FAULT_F_ADDRESS;
|
||||||
|
virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_MAPPING,
|
||||||
|
flags | VIRTIO_IOMMU_FAULT_F_ADDRESS,
|
||||||
|
sid, addr);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
entry.translated_addr = addr - mapping_key->low + mapping_value->phys_addr;
|
||||||
|
entry.perm = flag;
|
||||||
|
trace_virtio_iommu_translate_out(addr, entry.translated_addr, sid);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
qemu_mutex_unlock(&s->mutex);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
|
||||||
|
{
|
||||||
|
guint ua = GPOINTER_TO_UINT(a);
|
||||||
|
guint ub = GPOINTER_TO_UINT(b);
|
||||||
|
return (ua > ub) - (ua < ub);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
memset(s->iommu_pcibus_by_bus_num, 0, sizeof(s->iommu_pcibus_by_bus_num));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free);
|
||||||
|
|
||||||
|
if (s->primary_bus) {
|
||||||
|
pci_setup_iommu(s->primary_bus, virtio_iommu_find_add_as, s);
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_device_unrealize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
|
VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
|
||||||
|
|
||||||
|
g_tree_destroy(s->domains);
|
||||||
|
g_tree_destroy(s->endpoints);
|
||||||
|
|
||||||
|
virtio_cleanup(vdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_iommu_device_reset(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
VirtIOIOMMU *s = VIRTIO_IOMMU(vdev);
|
||||||
|
|
||||||
|
trace_virtio_iommu_device_reset();
|
||||||
|
|
||||||
|
if (s->domains) {
|
||||||
|
g_tree_destroy(s->domains);
|
||||||
|
}
|
||||||
|
if (s->endpoints) {
|
||||||
|
g_tree_destroy(s->endpoints);
|
||||||
|
}
|
||||||
|
s->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
|
||||||
|
NULL, NULL, virtio_iommu_put_domain);
|
||||||
|
s->endpoints = g_tree_new_full((GCompareDataFunc)int_cmp,
|
||||||
|
NULL, NULL, virtio_iommu_put_endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VMSTATE_INTERVAL \
|
||||||
|
{ \
|
||||||
|
.name = "interval", \
|
||||||
|
.version_id = 1, \
|
||||||
|
.minimum_version_id = 1, \
|
||||||
|
.fields = (VMStateField[]) { \
|
||||||
|
VMSTATE_UINT64(low, VirtIOIOMMUInterval), \
|
||||||
|
VMSTATE_UINT64(high, VirtIOIOMMUInterval), \
|
||||||
|
VMSTATE_END_OF_LIST() \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VMSTATE_MAPPING \
|
||||||
|
{ \
|
||||||
|
.name = "mapping", \
|
||||||
|
.version_id = 1, \
|
||||||
|
.minimum_version_id = 1, \
|
||||||
|
.fields = (VMStateField[]) { \
|
||||||
|
VMSTATE_UINT64(phys_addr, VirtIOIOMMUMapping),\
|
||||||
|
VMSTATE_UINT32(flags, VirtIOIOMMUMapping), \
|
||||||
|
VMSTATE_END_OF_LIST() \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_interval_mapping[2] = {
|
||||||
|
VMSTATE_MAPPING, /* value */
|
||||||
|
VMSTATE_INTERVAL /* key */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int domain_preload(void *opaque)
|
||||||
|
{
|
||||||
|
VirtIOIOMMUDomain *domain = opaque;
|
||||||
|
|
||||||
|
domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
|
||||||
|
NULL, g_free, g_free);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_endpoint = {
|
||||||
|
.name = "endpoint",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(id, VirtIOIOMMUEndpoint),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_domain = {
|
||||||
|
.name = "domain",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.pre_load = domain_preload,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(id, VirtIOIOMMUDomain),
|
||||||
|
VMSTATE_GTREE_V(mappings, VirtIOIOMMUDomain, 1,
|
||||||
|
vmstate_interval_mapping,
|
||||||
|
VirtIOIOMMUInterval, VirtIOIOMMUMapping),
|
||||||
|
VMSTATE_QLIST_V(endpoint_list, VirtIOIOMMUDomain, 1,
|
||||||
|
vmstate_endpoint, VirtIOIOMMUEndpoint, next),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean reconstruct_endpoints(gpointer key, gpointer value,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
VirtIOIOMMU *s = (VirtIOIOMMU *)data;
|
||||||
|
VirtIOIOMMUDomain *d = (VirtIOIOMMUDomain *)value;
|
||||||
|
VirtIOIOMMUEndpoint *iter;
|
||||||
|
|
||||||
|
QLIST_FOREACH(iter, &d->endpoint_list, next) {
|
||||||
|
iter->domain = d;
|
||||||
|
g_tree_insert(s->endpoints, GUINT_TO_POINTER(iter->id), iter);
|
||||||
|
}
|
||||||
|
return false; /* continue the domain traversal */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iommu_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
VirtIOIOMMU *s = opaque;
|
||||||
|
|
||||||
|
g_tree_foreach(s->domains, reconstruct_endpoints, s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_iommu_device = {
|
||||||
|
.name = "virtio-iommu-device",
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.version_id = 1,
|
||||||
|
.post_load = iommu_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 1,
|
||||||
|
&vmstate_domain, VirtIOIOMMUDomain),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_iommu = {
|
||||||
|
.name = "virtio-iommu",
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.priority = MIG_PRI_IOMMU,
|
||||||
|
.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 void virtio_iommu_memory_region_class_init(ObjectClass *klass,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
|
||||||
|
|
||||||
|
imrc->translate = virtio_iommu_translate;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 const TypeInfo virtio_iommu_memory_region_info = {
|
||||||
|
.parent = TYPE_IOMMU_MEMORY_REGION,
|
||||||
|
.name = TYPE_VIRTIO_IOMMU_MEMORY_REGION,
|
||||||
|
.class_init = virtio_iommu_memory_region_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void virtio_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&virtio_iommu_info);
|
||||||
|
type_register_static(&virtio_iommu_memory_region_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(virtio_register_types)
|
@ -130,6 +130,7 @@ static void virtio_pmem_unrealize(DeviceState *dev, Error **errp)
|
|||||||
VirtIOPMEM *pmem = VIRTIO_PMEM(dev);
|
VirtIOPMEM *pmem = VIRTIO_PMEM(dev);
|
||||||
|
|
||||||
host_memory_backend_set_mapped(pmem->memdev, false);
|
host_memory_backend_set_mapped(pmem->memdev, false);
|
||||||
|
virtio_delete_queue(pmem->rq_vq);
|
||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,15 +282,19 @@ static void vring_packed_flags_write(VirtIODevice *vdev,
|
|||||||
/* Called within rcu_read_lock(). */
|
/* Called within rcu_read_lock(). */
|
||||||
static VRingMemoryRegionCaches *vring_get_region_caches(struct VirtQueue *vq)
|
static VRingMemoryRegionCaches *vring_get_region_caches(struct VirtQueue *vq)
|
||||||
{
|
{
|
||||||
VRingMemoryRegionCaches *caches = atomic_rcu_read(&vq->vring.caches);
|
return atomic_rcu_read(&vq->vring.caches);
|
||||||
assert(caches != NULL);
|
|
||||||
return caches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called within rcu_read_lock(). */
|
/* Called within rcu_read_lock(). */
|
||||||
static inline uint16_t vring_avail_flags(VirtQueue *vq)
|
static inline uint16_t vring_avail_flags(VirtQueue *vq)
|
||||||
{
|
{
|
||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
hwaddr pa = offsetof(VRingAvail, flags);
|
hwaddr pa = offsetof(VRingAvail, flags);
|
||||||
|
|
||||||
|
if (!caches) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa);
|
return virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,6 +303,11 @@ static inline uint16_t vring_avail_idx(VirtQueue *vq)
|
|||||||
{
|
{
|
||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
hwaddr pa = offsetof(VRingAvail, idx);
|
hwaddr pa = offsetof(VRingAvail, idx);
|
||||||
|
|
||||||
|
if (!caches) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
vq->shadow_avail_idx = virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa);
|
vq->shadow_avail_idx = virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa);
|
||||||
return vq->shadow_avail_idx;
|
return vq->shadow_avail_idx;
|
||||||
}
|
}
|
||||||
@ -308,6 +317,11 @@ static inline uint16_t vring_avail_ring(VirtQueue *vq, int i)
|
|||||||
{
|
{
|
||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
hwaddr pa = offsetof(VRingAvail, ring[i]);
|
hwaddr pa = offsetof(VRingAvail, ring[i]);
|
||||||
|
|
||||||
|
if (!caches) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa);
|
return virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,6 +337,11 @@ static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem,
|
|||||||
{
|
{
|
||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
hwaddr pa = offsetof(VRingUsed, ring[i]);
|
hwaddr pa = offsetof(VRingUsed, ring[i]);
|
||||||
|
|
||||||
|
if (!caches) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
virtio_tswap32s(vq->vdev, &uelem->id);
|
virtio_tswap32s(vq->vdev, &uelem->id);
|
||||||
virtio_tswap32s(vq->vdev, &uelem->len);
|
virtio_tswap32s(vq->vdev, &uelem->len);
|
||||||
address_space_write_cached(&caches->used, pa, uelem, sizeof(VRingUsedElem));
|
address_space_write_cached(&caches->used, pa, uelem, sizeof(VRingUsedElem));
|
||||||
@ -334,6 +353,11 @@ static uint16_t vring_used_idx(VirtQueue *vq)
|
|||||||
{
|
{
|
||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
hwaddr pa = offsetof(VRingUsed, idx);
|
hwaddr pa = offsetof(VRingUsed, idx);
|
||||||
|
|
||||||
|
if (!caches) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return virtio_lduw_phys_cached(vq->vdev, &caches->used, pa);
|
return virtio_lduw_phys_cached(vq->vdev, &caches->used, pa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,8 +366,12 @@ static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val)
|
|||||||
{
|
{
|
||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
hwaddr pa = offsetof(VRingUsed, idx);
|
hwaddr pa = offsetof(VRingUsed, idx);
|
||||||
virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val);
|
|
||||||
address_space_cache_invalidate(&caches->used, pa, sizeof(val));
|
if (caches) {
|
||||||
|
virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val);
|
||||||
|
address_space_cache_invalidate(&caches->used, pa, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
vq->used_idx = val;
|
vq->used_idx = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,8 +381,13 @@ static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask)
|
|||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
VirtIODevice *vdev = vq->vdev;
|
VirtIODevice *vdev = vq->vdev;
|
||||||
hwaddr pa = offsetof(VRingUsed, flags);
|
hwaddr pa = offsetof(VRingUsed, flags);
|
||||||
uint16_t flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa);
|
uint16_t flags;
|
||||||
|
|
||||||
|
if (!caches) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa);
|
||||||
virtio_stw_phys_cached(vdev, &caches->used, pa, flags | mask);
|
virtio_stw_phys_cached(vdev, &caches->used, pa, flags | mask);
|
||||||
address_space_cache_invalidate(&caches->used, pa, sizeof(flags));
|
address_space_cache_invalidate(&caches->used, pa, sizeof(flags));
|
||||||
}
|
}
|
||||||
@ -365,8 +398,13 @@ static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask)
|
|||||||
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
|
||||||
VirtIODevice *vdev = vq->vdev;
|
VirtIODevice *vdev = vq->vdev;
|
||||||
hwaddr pa = offsetof(VRingUsed, flags);
|
hwaddr pa = offsetof(VRingUsed, flags);
|
||||||
uint16_t flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa);
|
uint16_t flags;
|
||||||
|
|
||||||
|
if (!caches) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa);
|
||||||
virtio_stw_phys_cached(vdev, &caches->used, pa, flags & ~mask);
|
virtio_stw_phys_cached(vdev, &caches->used, pa, flags & ~mask);
|
||||||
address_space_cache_invalidate(&caches->used, pa, sizeof(flags));
|
address_space_cache_invalidate(&caches->used, pa, sizeof(flags));
|
||||||
}
|
}
|
||||||
@ -381,6 +419,10 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pa = offsetof(VRingUsed, ring[vq->vring.num]);
|
pa = offsetof(VRingUsed, ring[vq->vring.num]);
|
||||||
virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val);
|
virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val);
|
||||||
address_space_cache_invalidate(&caches->used, pa, sizeof(val));
|
address_space_cache_invalidate(&caches->used, pa, sizeof(val));
|
||||||
@ -410,7 +452,11 @@ static void virtio_queue_packed_set_notification(VirtQueue *vq, int enable)
|
|||||||
VRingMemoryRegionCaches *caches;
|
VRingMemoryRegionCaches *caches;
|
||||||
|
|
||||||
RCU_READ_LOCK_GUARD();
|
RCU_READ_LOCK_GUARD();
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vring_packed_event_read(vq->vdev, &caches->used, &e);
|
vring_packed_event_read(vq->vdev, &caches->used, &e);
|
||||||
|
|
||||||
if (!enable) {
|
if (!enable) {
|
||||||
@ -597,6 +643,10 @@ static int virtio_queue_packed_empty_rcu(VirtQueue *vq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cache = vring_get_region_caches(vq);
|
cache = vring_get_region_caches(vq);
|
||||||
|
if (!cache) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
vring_packed_desc_read_flags(vq->vdev, &desc.flags, &cache->desc,
|
vring_packed_desc_read_flags(vq->vdev, &desc.flags, &cache->desc,
|
||||||
vq->last_avail_idx);
|
vq->last_avail_idx);
|
||||||
|
|
||||||
@ -777,6 +827,10 @@ static void virtqueue_packed_fill_desc(VirtQueue *vq,
|
|||||||
}
|
}
|
||||||
|
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vring_packed_desc_write(vq->vdev, &desc, &caches->desc, head, strict_order);
|
vring_packed_desc_write(vq->vdev, &desc, &caches->desc, head, strict_order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,6 +1003,10 @@ static void virtqueue_split_get_avail_bytes(VirtQueue *vq,
|
|||||||
|
|
||||||
max = vq->vring.num;
|
max = vq->vring.num;
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
while ((rc = virtqueue_num_heads(vq, idx)) > 0) {
|
while ((rc = virtqueue_num_heads(vq, idx)) > 0) {
|
||||||
MemoryRegionCache *desc_cache = &caches->desc;
|
MemoryRegionCache *desc_cache = &caches->desc;
|
||||||
unsigned int num_bufs;
|
unsigned int num_bufs;
|
||||||
@ -1089,6 +1147,9 @@ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq,
|
|||||||
|
|
||||||
max = vq->vring.num;
|
max = vq->vring.num;
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
unsigned int num_bufs = total_bufs;
|
unsigned int num_bufs = total_bufs;
|
||||||
@ -1194,6 +1255,10 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
|
|||||||
}
|
}
|
||||||
|
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
desc_size = virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED) ?
|
desc_size = virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED) ?
|
||||||
sizeof(VRingPackedDesc) : sizeof(VRingDesc);
|
sizeof(VRingPackedDesc) : sizeof(VRingDesc);
|
||||||
if (caches->desc.len < vq->vring.num * desc_size) {
|
if (caches->desc.len < vq->vring.num * desc_size) {
|
||||||
@ -1388,6 +1453,11 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
|
|||||||
i = head;
|
i = head;
|
||||||
|
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
virtio_error(vdev, "Region caches not initialized");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (caches->desc.len < max * sizeof(VRingDesc)) {
|
if (caches->desc.len < max * sizeof(VRingDesc)) {
|
||||||
virtio_error(vdev, "Cannot map descriptor ring");
|
virtio_error(vdev, "Cannot map descriptor ring");
|
||||||
goto done;
|
goto done;
|
||||||
@ -1510,6 +1580,11 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz)
|
|||||||
i = vq->last_avail_idx;
|
i = vq->last_avail_idx;
|
||||||
|
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
virtio_error(vdev, "Region caches not initialized");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (caches->desc.len < max * sizeof(VRingDesc)) {
|
if (caches->desc.len < max * sizeof(VRingDesc)) {
|
||||||
virtio_error(vdev, "Cannot map descriptor ring");
|
virtio_error(vdev, "Cannot map descriptor ring");
|
||||||
goto done;
|
goto done;
|
||||||
@ -1629,6 +1704,10 @@ static unsigned int virtqueue_packed_drop_all(VirtQueue *vq)
|
|||||||
VRingPackedDesc desc;
|
VRingPackedDesc desc;
|
||||||
|
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
desc_cache = &caches->desc;
|
desc_cache = &caches->desc;
|
||||||
|
|
||||||
virtio_queue_set_notification(vq, 0);
|
virtio_queue_set_notification(vq, 0);
|
||||||
@ -2413,6 +2492,10 @@ static bool virtio_packed_should_notify(VirtIODevice *vdev, VirtQueue *vq)
|
|||||||
VRingMemoryRegionCaches *caches;
|
VRingMemoryRegionCaches *caches;
|
||||||
|
|
||||||
caches = vring_get_region_caches(vq);
|
caches = vring_get_region_caches(vq);
|
||||||
|
if (!caches) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
vring_packed_event_read(vdev, &caches->avail, &e);
|
vring_packed_event_read(vdev, &caches->avail, &e);
|
||||||
|
|
||||||
old = vq->signalled_used;
|
old = vq->signalled_used;
|
||||||
|
@ -125,8 +125,10 @@ typedef struct {
|
|||||||
bool virt;
|
bool virt;
|
||||||
int32_t gic_version;
|
int32_t gic_version;
|
||||||
VirtIOMMUType iommu;
|
VirtIOMMUType iommu;
|
||||||
|
uint16_t virtio_iommu_bdf;
|
||||||
struct arm_boot_info bootinfo;
|
struct arm_boot_info bootinfo;
|
||||||
MemMapEntry *memmap;
|
MemMapEntry *memmap;
|
||||||
|
char *pciehb_nodename;
|
||||||
const int *irqmap;
|
const int *irqmap;
|
||||||
int smp_cpus;
|
int smp_cpus;
|
||||||
void *fdt;
|
void *fdt;
|
||||||
|
@ -86,6 +86,7 @@ extern bool pci_available;
|
|||||||
#define PCI_DEVICE_ID_VIRTIO_9P 0x1009
|
#define PCI_DEVICE_ID_VIRTIO_9P 0x1009
|
||||||
#define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012
|
#define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012
|
||||||
#define PCI_DEVICE_ID_VIRTIO_PMEM 0x1013
|
#define PCI_DEVICE_ID_VIRTIO_PMEM 0x1013
|
||||||
|
#define PCI_DEVICE_ID_VIRTIO_IOMMU 0x1014
|
||||||
|
|
||||||
#define PCI_VENDOR_ID_REDHAT 0x1b36
|
#define PCI_VENDOR_ID_REDHAT 0x1b36
|
||||||
#define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001
|
#define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001
|
||||||
|
@ -36,7 +36,8 @@ typedef struct VHostUserBlk {
|
|||||||
struct vhost_dev dev;
|
struct vhost_dev dev;
|
||||||
struct vhost_inflight *inflight;
|
struct vhost_inflight *inflight;
|
||||||
VhostUserState vhost_user;
|
VhostUserState vhost_user;
|
||||||
struct vhost_virtqueue *vqs;
|
struct vhost_virtqueue *vhost_vqs;
|
||||||
|
VirtQueue **virtqs;
|
||||||
guint watch;
|
guint watch;
|
||||||
bool connected;
|
bool connected;
|
||||||
} VHostUserBlk;
|
} VHostUserBlk;
|
||||||
|
@ -37,6 +37,8 @@ typedef struct {
|
|||||||
struct vhost_virtqueue *vhost_vqs;
|
struct vhost_virtqueue *vhost_vqs;
|
||||||
struct vhost_dev vhost_dev;
|
struct vhost_dev vhost_dev;
|
||||||
VhostUserState vhost_user;
|
VhostUserState vhost_user;
|
||||||
|
VirtQueue **req_vqs;
|
||||||
|
VirtQueue *hiprio_vq;
|
||||||
|
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
} VHostUserFS;
|
} VHostUserFS;
|
||||||
|
61
include/hw/virtio/virtio-iommu.h
Normal file
61
include/hw/virtio/virtio-iommu.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QEMU_VIRTIO_IOMMU_H
|
||||||
|
#define QEMU_VIRTIO_IOMMU_H
|
||||||
|
|
||||||
|
#include "standard-headers/linux/virtio_iommu.h"
|
||||||
|
#include "hw/virtio/virtio.h"
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
|
||||||
|
#define TYPE_VIRTIO_IOMMU "virtio-iommu-device"
|
||||||
|
#define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-device-base"
|
||||||
|
#define VIRTIO_IOMMU(obj) \
|
||||||
|
OBJECT_CHECK(VirtIOIOMMU, (obj), TYPE_VIRTIO_IOMMU)
|
||||||
|
|
||||||
|
#define TYPE_VIRTIO_IOMMU_MEMORY_REGION "virtio-iommu-memory-region"
|
||||||
|
|
||||||
|
typedef struct IOMMUDevice {
|
||||||
|
void *viommu;
|
||||||
|
PCIBus *bus;
|
||||||
|
int devfn;
|
||||||
|
IOMMUMemoryRegion iommu_mr;
|
||||||
|
AddressSpace as;
|
||||||
|
} IOMMUDevice;
|
||||||
|
|
||||||
|
typedef struct IOMMUPciBus {
|
||||||
|
PCIBus *bus;
|
||||||
|
IOMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */
|
||||||
|
} IOMMUPciBus;
|
||||||
|
|
||||||
|
typedef struct VirtIOIOMMU {
|
||||||
|
VirtIODevice parent_obj;
|
||||||
|
VirtQueue *req_vq;
|
||||||
|
VirtQueue *event_vq;
|
||||||
|
struct virtio_iommu_config config;
|
||||||
|
uint64_t features;
|
||||||
|
GHashTable *as_by_busptr;
|
||||||
|
IOMMUPciBus *iommu_pcibus_by_bus_num[PCI_BUS_MAX];
|
||||||
|
PCIBus *primary_bus;
|
||||||
|
GTree *domains;
|
||||||
|
QemuMutex mutex;
|
||||||
|
GTree *endpoints;
|
||||||
|
} VirtIOIOMMU;
|
||||||
|
|
||||||
|
#endif
|
@ -67,6 +67,7 @@ static const QDevAlias qdev_alias_table[] = {
|
|||||||
{ "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_S390X },
|
{ "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_S390X },
|
||||||
{ "virtio-input-host-pci", "virtio-input-host",
|
{ "virtio-input-host-pci", "virtio-input-host",
|
||||||
QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
|
QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
|
||||||
|
{ "virtio-iommu-pci", "virtio-iommu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
|
||||||
{ "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_S390X },
|
{ "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_S390X },
|
||||||
{ "virtio-keyboard-pci", "virtio-keyboard",
|
{ "virtio-keyboard-pci", "virtio-keyboard",
|
||||||
QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
|
QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
|
||||||
|
@ -31,6 +31,13 @@ done
|
|||||||
|
|
||||||
eval `grep SRC_PATH= config-host.mak`
|
eval `grep SRC_PATH= config-host.mak`
|
||||||
|
|
||||||
|
old_allowed_dif=`grep -v -e 'List of comma-separated changed AML files to ignore' ${SRC_PATH}/tests/qtest/bios-tables-test-allowed-diff.h`
|
||||||
|
|
||||||
echo '/* List of comma-separated changed AML files to ignore */' > ${SRC_PATH}/tests/qtest/bios-tables-test-allowed-diff.h
|
echo '/* List of comma-separated changed AML files to ignore */' > ${SRC_PATH}/tests/qtest/bios-tables-test-allowed-diff.h
|
||||||
|
|
||||||
echo "The files were rebuilt and can be added to git."
|
echo "The files were rebuilt and can be added to git."
|
||||||
|
|
||||||
|
if [ -z "$old_allowed_dif" ]; then
|
||||||
|
echo "Note! Please do not commit expected files with source changes"
|
||||||
|
echo "Note! Please follow the process documented in ${SRC_PATH}/tests/qtest/bios-tables-test.c"
|
||||||
|
fi
|
||||||
|
@ -426,7 +426,9 @@ static void test_acpi_asl(test_data *data)
|
|||||||
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"acpi-test: Warning! %.4s binary file mismatch. "
|
"acpi-test: Warning! %.4s binary file mismatch. "
|
||||||
"Actual [aml:%s], Expected [aml:%s].\n",
|
"Actual [aml:%s], Expected [aml:%s].\n"
|
||||||
|
"See source file tests/qtest/bios-tables-test.c "
|
||||||
|
"for instructions on how to update expected files.\n",
|
||||||
exp_sdt->aml, sdt->aml_file, exp_sdt->aml_file);
|
exp_sdt->aml, sdt->aml_file, exp_sdt->aml_file);
|
||||||
|
|
||||||
all_tables_match = all_tables_match &&
|
all_tables_match = all_tables_match &&
|
||||||
@ -461,21 +463,20 @@ static void test_acpi_asl(test_data *data)
|
|||||||
"Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n",
|
"Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n",
|
||||||
exp_sdt->aml, sdt->asl_file, sdt->aml_file,
|
exp_sdt->aml, sdt->asl_file, sdt->aml_file,
|
||||||
exp_sdt->asl_file, exp_sdt->aml_file);
|
exp_sdt->asl_file, exp_sdt->aml_file);
|
||||||
|
fflush(stderr);
|
||||||
if (getenv("V")) {
|
if (getenv("V")) {
|
||||||
const char *diff_cmd = getenv("DIFF");
|
const char *diff_env = getenv("DIFF");
|
||||||
if (diff_cmd) {
|
const char *diff_cmd = diff_env ? diff_env : "diff -u";
|
||||||
int ret G_GNUC_UNUSED;
|
char *diff = g_strdup_printf("%s %s %s", diff_cmd,
|
||||||
char *diff = g_strdup_printf("%s %s %s", diff_cmd,
|
exp_sdt->asl_file, sdt->asl_file);
|
||||||
exp_sdt->asl_file, sdt->asl_file);
|
int out = dup(STDOUT_FILENO);
|
||||||
ret = system(diff) ;
|
int ret G_GNUC_UNUSED;
|
||||||
g_free(diff);
|
|
||||||
} else {
|
dup2(STDERR_FILENO, STDOUT_FILENO);
|
||||||
fprintf(stderr, "acpi-test: Warning. not showing "
|
ret = system(diff) ;
|
||||||
"difference since no diff utility is specified. "
|
dup2(out, STDOUT_FILENO);
|
||||||
"Set 'DIFF' environment variable to a preferred "
|
close(out);
|
||||||
"diff utility and run 'make V=1 check' again to "
|
g_free(diff);
|
||||||
"see ASL difference.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user