diff --git a/drivers/virt/gunyah/gh_secure_vm_virtio_backend.c b/drivers/virt/gunyah/gh_secure_vm_virtio_backend.c new file mode 100644 index 000000000000..d1c5f25ee633 --- /dev/null +++ b/drivers/virt/gunyah/gh_secure_vm_virtio_backend.c @@ -0,0 +1,1318 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gh_secure_vm_virtio_backend.h" +#include "hcall_virtio.h" + +#define CREATE_TRACE_POINTS +#include +#undef CREATE_TRACE_POINTS + +#define MAX_QUEUES 4 +#define MAX_IO_CONTEXTS MAX_QUEUES + +#define VIRTIO_PRINT_MARKER "gh_virtio_backend" + +#define assert_virq gh_hcall_virtio_mmio_backend_assert_virq +#define set_dev_features gh_hcall_virtio_mmio_backend_set_dev_features +#define set_queue_num_max gh_hcall_virtio_mmio_backend_set_queue_num_max +#define get_drv_features gh_hcall_virtio_mmio_backend_get_drv_features +#define get_queue_info gh_hcall_virtio_mmio_backend_get_queue_info +#define get_event gh_hcall_virtio_mmio_backend_get_event +#define ack_reset gh_hcall_virtio_mmio_backend_ack_reset + +static DEFINE_MUTEX(vm_mutex); +static DEFINE_SPINLOCK(vm_list_lock); +static LIST_HEAD(vm_list); + +enum { + VM_STATE_NOT_ACTIVE, + VM_STATE_ACTIVE, +}; + +struct shared_memory { + struct resource r; + u32 gunyah_label, shm_memparcel; +}; + +struct virt_machine { + struct list_head list; + char vm_name[GH_VM_FW_NAME_MAX]; + int hyp_assign_done; + struct device *dev; + struct shared_memory *shmem; + int shmem_entries; + spinlock_t vb_dev_lock; + struct list_head vb_dev_list; + phys_addr_t shmem_addr; + u64 shmem_size; +}; + +struct ioevent_context { + int fd; + struct eventfd_ctx *ctx; +}; + +struct irq_context { + struct eventfd_ctx *ctx; + wait_queue_entry_t wait; + poll_table pt; + struct fd fd; + struct work_struct shutdown_work; +}; + +struct virtio_backend_device { + struct list_head list; + spinlock_t lock; + wait_queue_head_t evt_queue; + wait_queue_head_t notify_queue; + u32 refcount; + int notify; + int ack_driver_ok; + int evt_avail; + u64 cur_event_data, vdev_event_data; + u64 cur_event, vdev_event; + int linux_irq; + u32 label; + struct device *dev; + struct virt_machine *vm; + struct ioevent_context ioctx[MAX_IO_CONTEXTS]; + struct irq_context irq; + char int_name[32]; + u32 features[2]; + u32 queue_num_max[MAX_QUEUES]; + struct mutex mutex; + gh_capid_t cap_id; + /* Backend program supplied config data */ + char *config_data; + u32 config_size; + /* Page shared with frontend */ + char __iomem *config_shared_buf; + u64 config_shared_size; +}; + +static struct virt_machine *find_vm_by_name(const char *vm_name) +{ + struct virt_machine *v = NULL, *tmp; + + spin_lock(&vm_list_lock); + list_for_each_entry(tmp, &vm_list, list) { + if (!strcmp(tmp->vm_name, vm_name)) { + v = tmp; + break; + } + } + spin_unlock(&vm_list_lock); + + return v; +} + +static struct virtio_backend_device * +vb_dev_get(struct virt_machine *vm, u32 label) +{ + struct virtio_backend_device *vb_dev = NULL, *tmp; + + spin_lock(&vm->vb_dev_lock); + list_for_each_entry(tmp, &vm->vb_dev_list, list) { + if (label == tmp->label) { + if (tmp->refcount < U32_MAX) + vb_dev = tmp; + break; + } + } + if (vb_dev) + vb_dev->refcount++; + spin_unlock(&vm->vb_dev_lock); + + return vb_dev; +} + +static void vb_dev_put(struct virtio_backend_device *vb_dev) +{ + struct virt_machine *vm = vb_dev->vm; + + spin_lock(&vm->vb_dev_lock); + vb_dev->refcount--; + if (!vb_dev->refcount && vb_dev->notify) + wake_up(&vb_dev->notify_queue); + spin_unlock(&vm->vb_dev_lock); +} + +static void +irqfd_shutdown(struct work_struct *work) +{ + struct irq_context *irq = container_of(work, + struct irq_context, shutdown_work); + struct virtio_backend_device *vb_dev; + unsigned long iflags; + u64 isr; + + vb_dev = container_of(irq, struct virtio_backend_device, irq); + + spin_lock_irqsave(&vb_dev->lock, iflags); + if (irq->ctx) { + eventfd_ctx_remove_wait_queue(irq->ctx, &irq->wait, &isr); + eventfd_ctx_put(irq->ctx); + fdput(irq->fd); + irq->ctx = NULL; + irq->fd.file = NULL; + } + spin_unlock_irqrestore(&vb_dev->lock, iflags); +} + +static int vb_dev_irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, + int sync, void *key) +{ + struct irq_context *irq = container_of(wait, struct irq_context, wait); + struct virtio_backend_device *vb_dev; + __poll_t flags = key_to_poll(key); + + vb_dev = container_of(irq, struct virtio_backend_device, irq); + + if (flags & EPOLLIN) { + int rc = assert_virq(vb_dev->cap_id, 1); + + trace_gh_virtio_backend_irq_inj(vb_dev->label, rc); + } + + if (flags & EPOLLHUP) + queue_work(system_wq, &irq->shutdown_work); + + return 0; +} + +static void vb_dev_irqfd_ptable_queue_proc(struct file *file, + wait_queue_head_t *wqh, poll_table *pt) +{ + struct irq_context *irq = container_of(pt, struct irq_context, pt); + + add_wait_queue(wqh, &irq->wait); +} + +static int vb_dev_irqfd(struct virtio_backend_device *vb_dev, + struct virtio_irqfd *ifd) +{ + struct fd f; + int ret = -EBUSY; + struct eventfd_ctx *eventfd = NULL; + __poll_t events; + unsigned long flags; + + f.file = NULL; + + spin_lock_irqsave(&vb_dev->lock, flags); + + if (vb_dev->irq.fd.file) + goto fail; + + f = fdget(ifd->fd); + if (!f.file) + goto fail; + + eventfd = eventfd_ctx_fileget(f.file); + if (IS_ERR(eventfd)) { + ret = PTR_ERR(eventfd); + goto fail; + } + vb_dev->irq.ctx = eventfd; + vb_dev->irq.fd = f; + + spin_unlock_irqrestore(&vb_dev->lock, flags); + + init_waitqueue_func_entry(&vb_dev->irq.wait, vb_dev_irqfd_wakeup); + INIT_WORK(&vb_dev->irq.shutdown_work, irqfd_shutdown); + init_poll_funcptr(&vb_dev->irq.pt, vb_dev_irqfd_ptable_queue_proc); + events = vfs_poll(f.file, &vb_dev->irq.pt); + if (events & EPOLLIN) { + dev_dbg(vb_dev->vm->dev, + "%s: Premature injection of interrupt\n", + VIRTIO_PRINT_MARKER); + } + + return 0; + +fail: + if (eventfd && !IS_ERR(eventfd)) + eventfd_ctx_put(eventfd); + if (f.file) + fdput(f); + + spin_unlock_irqrestore(&vb_dev->lock, flags); + + return ret; +} + +static int vb_dev_ioeventfd(struct virtio_backend_device *vb_dev, + struct virtio_eventfd *efd) +{ + struct eventfd_ctx *ctx = NULL; + int i = efd->queue_num; + unsigned long flags; + int ret = -EBUSY; + + if (efd->fd <= 0) + return -EINVAL; + + spin_lock_irqsave(&vb_dev->lock, flags); + if (vb_dev->ioctx[i].fd > 0) + goto out; + + ctx = eventfd_ctx_fdget(efd->fd); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto out; + } + + vb_dev->ioctx[i].ctx = ctx; + vb_dev->ioctx[i].fd = efd->fd; + + ret = 0; + +out: + spin_unlock_irqrestore(&vb_dev->lock, flags); + + return ret; +} + +static void signal_vqs(struct virtio_backend_device *vb_dev) +{ + int i; + u64 flags; + + for (i = 0; i < MAX_IO_CONTEXTS; ++i) { + flags = 1 << i; + if ((vb_dev->vdev_event_data & flags) && vb_dev->ioctx[i].ctx) { + eventfd_signal(vb_dev->ioctx[i].ctx, 1); + vb_dev->vdev_event_data &= ~flags; + trace_gh_virtio_backend_queue_notify(vb_dev->label, i); + } + } +} + +long gh_virtio_backend_ioctl(const char *vm_name, unsigned int cmd, + unsigned long arg) +{ + struct virt_machine *vm = find_vm_by_name(vm_name); + struct virtio_backend_device *vb_dev; + void __user *argp = (void __user *)arg; + struct virtio_eventfd efd; + struct virtio_irqfd ifd; + struct virtio_dev_features f; + struct virtio_queue_max q; + struct virtio_ack_reset r; + struct virtio_config_data d; + struct virtio_queue_info qi; + struct gh_hcall_virtio_queue_info qinfo; + struct virtio_driver_features df; + struct virtio_event ve; + u64 features; + u32 label; + int ret = 0, i, nr_words; + unsigned long flags; + u64 org_event, org_data; + u32 *p, *x, *ack_reg; + + if (!vm) + return -EINVAL; + + switch (cmd) { + case GH_GET_SHARED_MEMORY_SIZE: + if (copy_to_user(argp, &vm->shmem_size, + sizeof(vm->shmem_size))) + return -EFAULT; + break; + + case GH_IOEVENTFD: + if (copy_from_user(&efd, argp, sizeof(efd))) + return -EFAULT; + + if (efd.queue_num >= MAX_IO_CONTEXTS) + return -EINVAL; + + if (efd.flags != VBE_ASSIGN_IOEVENTFD) + return -EOPNOTSUPP; + + vb_dev = vb_dev_get(vm, efd.label); + if (!vb_dev) + return -EINVAL; + + ret = vb_dev_ioeventfd(vb_dev, &efd); + + vb_dev_put(vb_dev); + + return ret; + + case GH_IRQFD: + if (copy_from_user(&ifd, argp, sizeof(ifd))) + return -EFAULT; + + if (!ifd.label || ifd.fd <= 0) + return -EINVAL; + + if (ifd.flags != VBE_ASSIGN_IRQFD) + return -EOPNOTSUPP; + + vb_dev = vb_dev_get(vm, ifd.label); + if (!vb_dev) + return -EINVAL; + + ret = vb_dev_irqfd(vb_dev, &ifd); + + vb_dev_put(vb_dev); + + return ret; + + case GH_WAIT_FOR_EVENT: + if (copy_from_user(&ve, argp, sizeof(ve))) + return -EFAULT; + + if (!ve.label) + return -EINVAL; + + vb_dev = vb_dev_get(vm, ve.label); + if (!vb_dev) + return -EINVAL; + +loop_back: + if (!vb_dev->evt_avail) { + ret = wait_event_interruptible(vb_dev->evt_queue, + vb_dev->evt_avail); + if (ret) { + vb_dev_put(vb_dev); + return ret; + } + } + + spin_lock_irqsave(&vb_dev->lock, flags); + + vb_dev->cur_event = 0; + vb_dev->cur_event_data = 0; + vb_dev->evt_avail = 0; + ack_reg = (u32 *)(vb_dev->config_shared_buf + VIRTIO_MMIO_INTERRUPT_ACK); + + org_event = vb_dev->vdev_event; + org_data = vb_dev->vdev_event_data; + + if (vb_dev->vdev_event & (EVENT_MODULE_EXIT | EVENT_VM_EXIT)) + vb_dev->cur_event = EVENT_APP_EXIT; + else if (vb_dev->vdev_event & EVENT_RESET_RQST) { + vb_dev->vdev_event &= ~EVENT_RESET_RQST; + vb_dev->cur_event = EVENT_RESET_RQST; + if (vb_dev->vdev_event) + vb_dev->evt_avail = 1; + } else if (vb_dev->vdev_event & EVENT_DRIVER_OK) { + vb_dev->vdev_event &= ~EVENT_DRIVER_OK; + if (!vb_dev->ack_driver_ok) { + vb_dev->cur_event = EVENT_DRIVER_OK; + if (vb_dev->vdev_event) + vb_dev->evt_avail = 1; + } + } else if (vb_dev->vdev_event & EVENT_NEW_BUFFER) { + vb_dev->vdev_event &= ~EVENT_NEW_BUFFER; + if (vb_dev->vdev_event_data && vb_dev->ack_driver_ok) + signal_vqs(vb_dev); + if (vb_dev->vdev_event) { + vb_dev->cur_event = vb_dev->vdev_event; + vb_dev->vdev_event = 0; + vb_dev->cur_event_data = vb_dev->vdev_event_data; + vb_dev->vdev_event_data = 0; + if (vb_dev->cur_event & EVENT_INTERRUPT_ACK) + vb_dev->cur_event_data = readl_relaxed(ack_reg); + } + } else if (vb_dev->vdev_event & EVENT_INTERRUPT_ACK) { + vb_dev->vdev_event &= ~EVENT_INTERRUPT_ACK; + vb_dev->vdev_event_data = 0; + vb_dev->cur_event = EVENT_INTERRUPT_ACK; + vb_dev->cur_event_data = readl_relaxed(ack_reg); + } + + spin_unlock_irqrestore(&vb_dev->lock, flags); + + trace_gh_virtio_backend_wait_event(vb_dev->label, vb_dev->cur_event, + org_event, vb_dev->cur_event_data, org_data); + + if (!vb_dev->cur_event) + goto loop_back; + + ve.event = (u32) vb_dev->cur_event; + ve.event_data = (u32) vb_dev->cur_event_data; + + vb_dev_put(vb_dev); + + if (copy_to_user(argp, &ve, sizeof(ve))) + return -EFAULT; + + break; + + case GH_GET_DRIVER_FEATURES: + if (copy_from_user(&df, argp, sizeof(df))) + return -EFAULT; + + if (!df.label || df.features_sel > 1) + return -EINVAL; + + vb_dev = vb_dev_get(vm, df.label); + if (!vb_dev) + return -EINVAL; + + if (!vb_dev->cap_id) { + vb_dev_put(vb_dev); + return -EINVAL; + } + + ret = get_drv_features(vb_dev->cap_id, df.features_sel, &features); + + vb_dev_put(vb_dev); + + dev_dbg(vm->dev, "%s: get_drv_feat %d/%x ret %d\n", + VIRTIO_PRINT_MARKER, df.features_sel, features, ret); + if (ret) + return ret; + + df.features = (u32) features; + if (copy_to_user(argp, &df, sizeof(df))) + return -EFAULT; + + break; + + case GH_GET_QUEUE_INFO: + if (copy_from_user(&qi, argp, sizeof(qi))) + return -EFAULT; + + if (!qi.label || qi.queue_sel >= MAX_QUEUES) + return -EINVAL; + + vb_dev = vb_dev_get(vm, qi.label); + if (!vb_dev) + return -EINVAL; + + if (!vb_dev->cap_id) { + vb_dev_put(vb_dev); + return -EINVAL; + } + + ret = get_queue_info(vb_dev->cap_id, qi.queue_sel, &qinfo); + vb_dev_put(vb_dev); + + dev_dbg(vm->dev, "%s: get_queue_info %d: que_num %d que_ready %d que_desc %llx\n", + VIRTIO_PRINT_MARKER, qi.queue_sel, qinfo.queue_num, + qinfo.queue_ready, qinfo.queue_desc); + dev_dbg(vm->dev, "%s: que_driver %llx que_device %llx ret %d\n", + VIRTIO_PRINT_MARKER, qinfo.queue_driver, qinfo.queue_device, ret); + + if (ret) + return ret; + + qi.queue_num = (u32) qinfo.queue_num; + qi.queue_ready = (u32) qinfo.queue_ready; + qi.queue_desc = qinfo.queue_desc; + qi.queue_driver = qinfo.queue_driver; + qi.queue_device = qinfo.queue_device; + + if (copy_to_user(argp, &qi, sizeof(qi))) + return -EFAULT; + + break; + + case GH_ACK_DRIVER_OK: + label = (u32) arg; + + if (!label) + return -EINVAL; + + vb_dev = vb_dev_get(vm, label); + if (!vb_dev) + return -EINVAL; + + vb_dev->ack_driver_ok = 1; + vb_dev_put(vb_dev); + dev_dbg(vm->dev, "%s: ack_driver_ok for label %x!\n", VIRTIO_PRINT_MARKER, label); + + break; + + case GH_ACK_RESET: + if (copy_from_user(&r, argp, sizeof(r))) + return -EFAULT; + + if (!r.label) + return -EINVAL; + + vb_dev = vb_dev_get(vm, r.label); + if (!vb_dev || !vb_dev->cap_id) + return -EINVAL; + + ack_reset(vb_dev->cap_id); + vb_dev_put(vb_dev); + + dev_dbg(vm->dev, "%s: ack_reset for label %x!\n", VIRTIO_PRINT_MARKER, r.label); + break; + + case GH_SET_DEVICE_FEATURES: + if (copy_from_user(&f, argp, sizeof(f))) + return -EFAULT; + + if (!f.label || f.features_sel > 1) + return -EINVAL; + + vb_dev = vb_dev_get(vm, f.label); + if (!vb_dev) + return -EINVAL; + + vb_dev->features[f.features_sel] = f.features; + + ret = set_dev_features(vb_dev->cap_id, f.features_sel, + vb_dev->features[f.features_sel]); + if (ret) { + vb_dev_put(vb_dev); + dev_err(vm->dev, "%s: set_features %d/%x failed ret %d\n", + VIRTIO_PRINT_MARKER, f.features_sel, + vb_dev->features[f.features_sel], ret); + return ret; + } + + vb_dev_put(vb_dev); + + dev_dbg(vm->dev, "%s: label %d features %d %x\n", VIRTIO_PRINT_MARKER, + f.label, f.features_sel, f.features); + break; + + case GH_SET_QUEUE_NUM_MAX: + if (copy_from_user(&q, argp, sizeof(q))) + return -EFAULT; + + if (!q.label || q.queue_sel >= MAX_QUEUES) + return -EINVAL; + + vb_dev = vb_dev_get(vm, q.label); + if (!vb_dev) + return -EINVAL; + + vb_dev->queue_num_max[q.queue_sel] = q.queue_num_max; + + ret = set_queue_num_max(vb_dev->cap_id, q.queue_sel, + vb_dev->queue_num_max[q.queue_sel]); + if (ret) { + vb_dev_put(vb_dev); + dev_err(vm->dev, "%s: set_queue_num_max %d/%x failed ret %d\n", + VIRTIO_PRINT_MARKER, q.queue_sel, + vb_dev->queue_num_max[q.queue_sel], + ret); + return ret; + } + + vb_dev_put(vb_dev); + + dev_dbg(vm->dev, "%s: label %d queue_max %d %x\n", VIRTIO_PRINT_MARKER, + q.label, q.queue_sel, q.queue_num_max); + + break; + + case GH_GET_DRIVER_CONFIG_DATA: + if (copy_from_user(&d, argp, sizeof(d))) + return -EFAULT; + + vb_dev = vb_dev_get(vm, d.label); + if (!vb_dev) + return -EINVAL; + + mutex_lock(&vb_dev->mutex); + if (!d.label || d.config_size > vb_dev->config_shared_size || + !d.config_size || !d.config_data) { + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -EINVAL; + } + + if (!vb_dev->config_shared_buf) { + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -EINVAL; + } + ret = copy_to_user((char __user *)d.config_data, + vb_dev->config_shared_buf, d.config_size); + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return ret; + + case GH_SET_DEVICE_CONFIG_DATA: + if (copy_from_user(&d, argp, sizeof(d))) + return -EFAULT; + + vb_dev = vb_dev_get(vm, d.label); + if (!vb_dev) + return -EINVAL; + + mutex_lock(&vb_dev->mutex); + if (!d.label || d.config_size > vb_dev->config_shared_size || + !d.config_size || !d.config_data) { + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -EINVAL; + } + + if (vb_dev->config_data) { + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -EBUSY; + } + + if (!vb_dev->config_shared_buf) { + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -EINVAL; + } + + vb_dev->config_data = (char *)__get_free_pages( + GFP_KERNEL | __GFP_ZERO, 0); + if (!vb_dev->config_data) { + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -ENOMEM; + } + + if (copy_from_user(vb_dev->config_data, + u64_to_user_ptr(d.config_data), d.config_size)) { + free_pages((unsigned long)vb_dev->config_data, 0); + vb_dev->config_data = NULL; + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -EFAULT; + } + vb_dev->config_size = d.config_size; + p = (u32 *)vb_dev->config_data; + for (i = 0; i < 4; ++i) + dev_dbg(vm->dev, "%s: %x: %x\n", VIRTIO_PRINT_MARKER, i*4, p[i]); + + p = (u32 *)vb_dev->config_shared_buf; + x = (u32 *)vb_dev->config_data; + nr_words = vb_dev->config_size / 4; + + for (i = 0; i < nr_words; ++i) + writel_relaxed(*x++, p++); + + p = (u32 *)vb_dev->config_shared_buf; + for (i = 0; i < nr_words; ++i) { + dev_dbg(vm->dev, "%s: config_word %d %x\n", + VIRTIO_PRINT_MARKER, i, + readl_relaxed(p++)); + } + + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + break; + + default: + dev_err(vm->dev, "%s: cmd %x not supported\n", VIRTIO_PRINT_MARKER, cmd); + return -EINVAL; + } + + return 0; +} + +int gh_virtio_backend_mmap(const char *vm_name, + struct vm_area_struct *vma) +{ + struct virt_machine *vm = find_vm_by_name(vm_name); + size_t mmap_size; + + if (!vm) + return -EINVAL; + + mmap_size = vma->vm_end - vma->vm_start; + if (mmap_size != vm->shmem_size) + return -EINVAL; + + vma->vm_flags = vma->vm_flags | VM_DONTEXPAND | VM_DONTDUMP; + + if (io_remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(vm->shmem_addr), + mmap_size, vma->vm_page_prot)) { + dev_err(vm->dev, "%s: ioremap_pfn_range failed\n", VIRTIO_PRINT_MARKER); + return -EAGAIN; + } + + return 0; +} + +static void init_vb_dev_open(struct virtio_backend_device *vb_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&vb_dev->lock, flags); + vb_dev->evt_avail = 0; + vb_dev->cur_event_data = 0; + vb_dev->vdev_event_data = 0; + vb_dev->cur_event = 0; + vb_dev->vdev_event = 0; + vb_dev->notify = 0; + spin_unlock_irqrestore(&vb_dev->lock, flags); +} + +static int +note_shared_buffers(struct device_node *np, struct virt_machine *vm) +{ + int idx = 0; + u32 len, nr_entries = 0; + + if (of_find_property(np, "shared-buffers", &len)) + nr_entries = len / 4; + + if (!nr_entries) { + dev_err(vm->dev, "%s: No shared-buffers specified for vm %s\n", + VIRTIO_PRINT_MARKER, vm->vm_name); + return -EINVAL; + } + + vm->shmem = devm_kzalloc(vm->dev, nr_entries * sizeof(struct shared_memory), + GFP_KERNEL); + if (!vm->shmem) + return -ENOMEM; + + for (idx = 0; idx < nr_entries; ++idx) { + struct device_node *snp; + int ret; + + snp = of_parse_phandle(np, "shared-buffers", idx); + if (!snp) { + dev_err(vm->dev, "%s: Can't parse shared-buffer property %d\n", + VIRTIO_PRINT_MARKER, idx); + return -EINVAL; + } + + ret = of_address_to_resource(snp, 0, &vm->shmem[idx].r); + if (ret) { + of_node_put(snp); + dev_err(vm->dev, "%s: Invalid address at index %d\n", + VIRTIO_PRINT_MARKER, idx); + return -EINVAL; + } + + ret = of_property_read_u32(snp, "gunyah-label", + &vm->shmem[idx].gunyah_label); + if (ret) { + of_node_put(snp); + dev_err(vm->dev, "%s: gunyah-label property absent at index %d\n", + VIRTIO_PRINT_MARKER, idx); + return -EINVAL; + } + + of_node_put(snp); + } + + vm->shmem_entries = nr_entries; + + return 0; +} + +static struct virt_machine * +new_vm(struct device *dev, const char *vm_name, struct device_node *np) +{ + struct virt_machine *vm; + int ret; + struct resource r; + + ret = of_address_to_resource(np, 0, &r); + if (ret || !r.start || !resource_size(&r)) { + pr_err("%s: Invalid shared memory for VM %s\n", + VIRTIO_PRINT_MARKER, vm_name); + return NULL; + } + + vm = devm_kzalloc(dev, sizeof(*vm), GFP_KERNEL); + if (!vm) + return NULL; + + strscpy(vm->vm_name, vm_name, sizeof(vm->vm_name)); + vm->dev = dev; + ret = note_shared_buffers(np, vm); + if (ret) + return NULL; + + vm->shmem_addr = r.start; + vm->shmem_size = resource_size(&r); + + spin_lock_init(&vm->vb_dev_lock); + INIT_LIST_HEAD(&vm->vb_dev_list); + + spin_lock(&vm_list_lock); + list_add(&vm->list, &vm_list); + spin_unlock(&vm_list_lock); + + pr_info("%s: Recognized VM '%s' shmem_addr %pK shmem_size %llx\n", + VIRTIO_PRINT_MARKER, vm->vm_name, + (void *)vm->shmem_addr, vm->shmem_size); + + return vm; +} + +static int gh_virtio_backend_probe(struct device *dev, struct device_node *np, + const char *str) +{ + int ret; + struct device_node *vm_np; + struct virtio_backend_device *vb_dev = NULL, *tmp; + struct virt_machine *vm; + u32 label; + + if (!np || !str || !dev) + return -EINVAL; + + vm_np = of_parse_phandle(np, "qcom,vm", 0); + if (!vm_np) { + dev_err(dev, "%s: 'qcom,vm' property not present\n", + VIRTIO_PRINT_MARKER); + return -EINVAL; + } + + if (strnlen(str, GH_VM_FW_NAME_MAX) == GH_VM_FW_NAME_MAX) { + dev_err(dev, "%s: VM name %s too long\n", VIRTIO_PRINT_MARKER, str); + of_node_put(vm_np); + return -EINVAL; + } + + ret = of_property_read_u32(np, "qcom,label", &label); + if (ret || !label) { + dev_err(dev, "%s: Invalid qcom,label property\n", + VIRTIO_PRINT_MARKER); + of_node_put(vm_np); + return -EINVAL; + } + + mutex_lock(&vm_mutex); + vm = find_vm_by_name(str); + if (!vm) { + vm = new_vm(dev, str, vm_np); + if (!vm) { + of_node_put(vm_np); + mutex_unlock(&vm_mutex); + return -ENODEV; + } + } + + spin_lock(&vm->vb_dev_lock); + list_for_each_entry(tmp, &vm->vb_dev_list, list) { + if (label == tmp->label) { + vb_dev = tmp; + break; + } + } + spin_unlock(&vm->vb_dev_lock); + + if (vb_dev) { + vb_dev_put(vb_dev); + of_node_put(vm_np); + mutex_unlock(&vm_mutex); + dev_err(dev, "%s: Duplicate qcom,label %d\n", + VIRTIO_PRINT_MARKER, label); + return -EINVAL; + } + + vb_dev = devm_kzalloc(dev, sizeof(*vb_dev), + GFP_KERNEL | __GFP_ZERO); + if (!vb_dev) { + of_node_put(vm_np); + mutex_unlock(&vm_mutex); + return -ENOMEM; + } + + vb_dev->label = label; + vb_dev->vm = vm; + spin_lock_init(&vb_dev->lock); + init_waitqueue_head(&vb_dev->evt_queue); + init_waitqueue_head(&vb_dev->notify_queue); + mutex_init(&vb_dev->mutex); + + spin_lock(&vm->vb_dev_lock); + list_add(&vb_dev->list, &vm->vb_dev_list); + spin_unlock(&vm->vb_dev_lock); + + of_node_put(vm_np); + + snprintf(vb_dev->int_name, sizeof(vb_dev->int_name), + "virtio_dev_%x", vb_dev->label); + + mutex_unlock(&vm_mutex); + + pr_info("%s: %s: Recognized virtio backend device with label %x\n", + VIRTIO_PRINT_MARKER, vm->vm_name, vb_dev->label); + return 0; +} + +static void handle_probe_failure(const char *vm_name) +{ + struct virt_machine *vm; + + mutex_lock(&vm_mutex); + vm = find_vm_by_name(vm_name); + if (!vm) + goto done; + spin_lock(&vm_list_lock); + list_del(&vm->list); + spin_unlock(&vm_list_lock); +done: + mutex_unlock(&vm_mutex); +} + +int gh_parse_virtio_properties(struct device *dev, const char *vm_name) +{ + struct device_node *np = dev->of_node; + int idx = 0; + u32 len, nr_entries = 0; + + if (!dev || !vm_name) + return -EINVAL; + + if (of_find_property(np, "virtio-backends", &len)) + nr_entries = len / 4; + if (!nr_entries) { + dev_dbg(dev, "%s: No virtio-backends specified for vm %s\n", + VIRTIO_PRINT_MARKER, vm_name); + return 0; + } + for (idx = 0; idx < nr_entries; ++idx) { + struct device_node *snp; + int ret; + + snp = of_parse_phandle(np, "virtio-backends", idx); + if (!snp) { + dev_err(dev, "%s: Can't parse virtio-backends property %d\n", + VIRTIO_PRINT_MARKER, idx); + return -EINVAL; + } + ret = gh_virtio_backend_probe(dev, snp, vm_name); + if (ret) { + of_node_put(snp); + handle_probe_failure(vm_name); + return ret; + } + of_node_put(snp); + + } + return 0; +} + +int gh_virtio_backend_remove(const char *vm_name) +{ + struct virt_machine *vm; + + vm = find_vm_by_name(vm_name); + if (!vm) { + pr_debug("%s: VM name %s not found\n", VIRTIO_PRINT_MARKER, vm_name); + return 0; + } + spin_lock(&vm_list_lock); + list_del(&vm->list); + spin_unlock(&vm_list_lock); + return 0; +} + +static irqreturn_t vdev_interrupt(int irq, void *data) +{ + struct virtio_backend_device *vb_dev = data; + u64 event_data, event; + int ret; + unsigned long flags; + + ret = get_event(vb_dev->cap_id, &event_data, &event); + trace_gh_virtio_backend_irq(vb_dev->label, event, event_data, ret); + if (ret || !event) + return IRQ_HANDLED; + + spin_lock_irqsave(&vb_dev->lock, flags); + if (event == EVENT_NEW_BUFFER && vb_dev->ack_driver_ok) { + vb_dev->vdev_event_data = event_data; + signal_vqs(vb_dev); + goto done; + } + vb_dev->vdev_event |= event; + vb_dev->vdev_event_data |= event_data; + vb_dev->evt_avail = 1; + wake_up_interruptible(&vb_dev->evt_queue); +done: + spin_unlock_irqrestore(&vb_dev->lock, flags); + + return IRQ_HANDLED; +} + +static int +unshare_a_vm_buffer(gh_vmid_t self, gh_vmid_t peer, struct resource *r, + struct shared_memory *shmem) +{ + u32 src_vmlist[2] = {self, peer}; + int dst_vmlist[1] = {self}; + int dst_perms[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; + int ret; + + ret = gh_rm_mem_reclaim(shmem->shm_memparcel, 0); + if (ret) { + pr_err("%s: gh_rm_mem_reclaim failed for handle %x addr=%llx size=%lld err=%d\n", + VIRTIO_PRINT_MARKER, shmem->shm_memparcel, r->start, + resource_size(r), ret); + return ret; + } + + ret = hyp_assign_phys(r->start, resource_size(r), src_vmlist, 2, + dst_vmlist, dst_perms, 1); + if (ret) + pr_err("%s: hyp_assign_phys failed for addr=%llx size=%lld err=%d\n", + VIRTIO_PRINT_MARKER, r->start, resource_size(r), ret); + + return ret; +} + +static int unshare_vm_buffers(struct virt_machine *vm, gh_vmid_t peer) +{ + int ret, i; + gh_vmid_t self_vmid; + + if (!vm->hyp_assign_done) + return 0; + + ret = gh_rm_get_vmid(GH_PRIMARY_VM, &self_vmid); + if (ret) + return ret; + + for (i = 0; i < vm->shmem_entries; ++i) { + ret = unshare_a_vm_buffer(self_vmid, peer, &vm->shmem[i].r, + &vm->shmem[i]); + if (ret) + return ret; + } + vm->hyp_assign_done = 0; + return 0; +} + +static int share_a_vm_buffer(gh_vmid_t self, gh_vmid_t peer, int gunyah_label, + struct resource *r, u32 *shm_memparcel, + struct shared_memory *shmem) +{ + u32 src_vmlist[1] = {self}; + int src_perms[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; + int dst_vmlist[2] = {self, peer}; + int dst_perms[2] = {PERM_READ | PERM_WRITE, PERM_READ | PERM_WRITE}; + struct gh_acl_desc *acl; + struct gh_sgl_desc *sgl; + int ret; + + acl = kzalloc(offsetof(struct gh_acl_desc, acl_entries[2]), GFP_KERNEL); + if (!acl) + return -ENOMEM; + sgl = kzalloc(offsetof(struct gh_sgl_desc, sgl_entries[1]), GFP_KERNEL); + if (!sgl) { + kfree(acl); + return -ENOMEM; + } + + ret = hyp_assign_phys(r->start, resource_size(r), src_vmlist, 1, + dst_vmlist, dst_perms, 2); + if (ret) { + pr_err("%s: hyp_assign_phys failed for addr=%llx size=%lld err=%d\n", + VIRTIO_PRINT_MARKER, r->start, resource_size(r), ret); + kfree(acl); + kfree(sgl); + return ret; + } + + acl->n_acl_entries = 2; + acl->acl_entries[0].vmid = (u16)self; + acl->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W; + acl->acl_entries[1].vmid = (u16)peer; + acl->acl_entries[1].perms = GH_RM_ACL_R | GH_RM_ACL_W; + + sgl->n_sgl_entries = 1; + sgl->sgl_entries[0].ipa_base = r->start; + sgl->sgl_entries[0].size = resource_size(r); + + ret = gh_rm_mem_share(GH_RM_MEM_TYPE_NORMAL, 0, gunyah_label, + acl, sgl, NULL, shm_memparcel); + if (ret) { + pr_err("%s: Sharing memory failed %d\n", VIRTIO_PRINT_MARKER, ret); + /* Attempt to assign resource back to HLOS */ + hyp_assign_phys(r->start, resource_size(r), dst_vmlist, 2, + src_vmlist, src_perms, 1); + } + + kfree(acl); + kfree(sgl); + + return ret; +} + +static int share_vm_buffers(struct virt_machine *vm, gh_vmid_t peer) +{ + int i, ret; + gh_vmid_t self_vmid; + + ret = gh_rm_get_vmid(GH_PRIMARY_VM, &self_vmid); + if (ret) + return ret; + + for (i = 0; i < vm->shmem_entries; ++i) { + ret = share_a_vm_buffer(self_vmid, peer, vm->shmem[i].gunyah_label, + &vm->shmem[i].r, &vm->shmem[i].shm_memparcel, &vm->shmem[i]); + if (ret) { + i--; + goto unshare; + } + } + + vm->hyp_assign_done = 1; + return 0; + +unshare: + for (; i >= 0; --i) + unshare_a_vm_buffer(self_vmid, peer, &vm->shmem[i].r, &vm->shmem[i]); + + return ret; +} + +static int gh_virtio_mmio_init(gh_vmid_t vmid, const char *vm_name, gh_label_t label, + gh_capid_t cap_id, int linux_irq, u64 base, u64 size) +{ + struct virt_machine *vm; + struct virtio_backend_device *vb_dev; + int ret; + + + vm = find_vm_by_name(vm_name); + if (!vm) { + pr_err("%s: VM name %s not found\n", VIRTIO_PRINT_MARKER, vm_name); + return -ENODEV; + } + + dev_dbg(vm->dev, "%s: vmid %d vm_name %s label %x cap_id %x irq %d base %pK size %d\n", + VIRTIO_PRINT_MARKER, vmid, vm_name, label, cap_id, linux_irq, (void *)base, size); + + if (strnlen(vm_name, GH_VM_FW_NAME_MAX) == GH_VM_FW_NAME_MAX) { + dev_dbg(vm->dev, "%s: VM name %s too long\n", VIRTIO_PRINT_MARKER, vm_name); + return -EINVAL; + } + + vb_dev = vb_dev_get(vm, label); + if (!vb_dev) { + dev_err(vm->dev, "%s: Device with label %x not found\n", +VIRTIO_PRINT_MARKER, label); + return -ENODEV; + } + + mutex_lock(&vb_dev->mutex); + + if (!vm->hyp_assign_done) { + ret = share_vm_buffers(vm, vmid); + if (ret) { + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return ret; + } + } + + ret = request_irq(linux_irq, vdev_interrupt, 0, + vb_dev->int_name, vb_dev); + if (ret) { + dev_err(vm->dev, "%s: Unable to register interrupt handler for %d\n", + VIRTIO_PRINT_MARKER, linux_irq); + unshare_vm_buffers(vm, vmid); + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return ret; + } + + vb_dev->config_shared_buf = ioremap(base, size); + if (!vb_dev->config_shared_buf) { + dev_err(vm->dev, "%s: Unable to map config page\n", VIRTIO_PRINT_MARKER); + free_irq(linux_irq, vb_dev); + unshare_vm_buffers(vm, vmid); + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + return -ENOMEM; + } + + vb_dev->linux_irq = linux_irq; + vb_dev->cap_id = cap_id; + vb_dev->config_shared_size = size; + init_vb_dev_open(vb_dev); + mutex_unlock(&vb_dev->mutex); + vb_dev_put(vb_dev); + + return 0; +} + +int gh_virtio_mmio_exit(gh_vmid_t vmid, const char *vm_name) +{ + struct virt_machine *vm; + struct virtio_backend_device *vb_dev; + unsigned long flags; + int ret = -EINVAL; + u32 refcount; + + vm = find_vm_by_name(vm_name); + if (!vm) { + pr_debug("%s: VM name %s not found\n", VIRTIO_PRINT_MARKER, vm_name); + return 0; + } + + spin_lock(&vm->vb_dev_lock); + list_for_each_entry(vb_dev, &vm->vb_dev_list, list) { + spin_lock_irqsave(&vb_dev->lock, flags); + vb_dev->vdev_event = EVENT_VM_EXIT; + vb_dev->vdev_event_data = 0; + vb_dev->evt_avail = 1; + vb_dev->notify = 1; + refcount = vb_dev->refcount; + spin_unlock_irqrestore(&vb_dev->lock, flags); + + wake_up_interruptible(&vb_dev->evt_queue); + spin_unlock(&vm->vb_dev_lock); + if (refcount) + wait_event(vb_dev->notify_queue, !vb_dev->refcount); + + free_irq(vb_dev->linux_irq, vb_dev); + iounmap(vb_dev->config_shared_buf); + vb_dev->config_shared_buf = NULL; + spin_lock(&vm->vb_dev_lock); + } + + spin_unlock(&vm->vb_dev_lock); + + ret = unshare_vm_buffers(vm, vmid); + + return ret; +} + +int __init gh_virtio_backend_init(void) +{ + return gh_rm_set_virtio_mmio_cb(gh_virtio_mmio_init); +} + +void __exit gh_virtio_backend_exit(void) +{ + gh_rm_unset_virtio_mmio_cb(); +} diff --git a/drivers/virt/gunyah/gh_secure_vm_virtio_backend.h b/drivers/virt/gunyah/gh_secure_vm_virtio_backend.h new file mode 100644 index 000000000000..39876b137f8b --- /dev/null +++ b/drivers/virt/gunyah/gh_secure_vm_virtio_backend.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _GH_SECURE_VM_VIRTIO_BACKEND_H +#define _GH_SECURE_VM_VIRTIO_BACKEND_H + +#include + +int gh_virtio_backend_mmap(const char *vm_name, struct vm_area_struct *vma); +long gh_virtio_backend_ioctl(const char *vm_name, unsigned int cmd, + unsigned long arg); +int gh_parse_virtio_properties(struct device *dev, const char *vm_name); +int gh_virtio_backend_remove(const char *vm_name); +int gh_virtio_mmio_exit(gh_vmid_t vmid, const char *vm_name); +int gh_virtio_backend_init(void); +void gh_virtio_backend_exit(void); + +#endif /* _GH_SECURE_VM_VIRTIO_BACKEND_H */ diff --git a/drivers/virt/gunyah/hcall_virtio.h b/drivers/virt/gunyah/hcall_virtio.h new file mode 100644 index 000000000000..c34484896c7d --- /dev/null +++ b/drivers/virt/gunyah/hcall_virtio.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ +#ifndef __GH_HCALL_VIRTIO_H +#define __GH_HCALL_VIRTIO_H + +#include +#include + +#include +#include +#include + +static inline int +gh_hcall_virtio_mmio_backend_assert_virq(gh_capid_t capid, u64 int_status) +{ + struct gh_hcall_resp _resp = {0}; + + return _gh_hcall(0x604e, (struct gh_hcall_args){ capid, int_status, 0 }, + &_resp); +} + +static inline int +gh_hcall_virtio_mmio_backend_set_dev_features(gh_capid_t capid, + u64 features_sel, u64 features) +{ + struct gh_hcall_resp _resp = {0}; + + return _gh_hcall(0x604f, + (struct gh_hcall_args){ capid, features_sel, features, + 0 }, + &_resp); +} + +static inline int +gh_hcall_virtio_mmio_backend_set_queue_num_max(gh_capid_t capid, + u64 queue_sel, u64 queue_num_max) +{ + struct gh_hcall_resp _resp = {0}; + + return _gh_hcall(0x6050, + (struct gh_hcall_args){ capid, queue_sel, + queue_num_max, 0 }, + &_resp); +} + +static inline int +gh_hcall_virtio_mmio_backend_get_drv_features(gh_capid_t capid, + u64 features_sel, u64 *features) +{ + int ret; + struct gh_hcall_resp _resp = {0}; + + ret = _gh_hcall(0x6051, + (struct gh_hcall_args){ capid, features_sel, 0}, + &_resp); + + if (!ret && features) + *features = _resp.resp1; + + return ret; +} + +struct gh_hcall_virtio_queue_info { + u64 queue_num; + u64 queue_ready; + u64 queue_desc; + u64 queue_driver; + u64 queue_device; +}; + +static inline int +gh_hcall_virtio_mmio_backend_get_queue_info(gh_capid_t capid, + u64 queue_sel, struct gh_hcall_virtio_queue_info *queue_info) +{ + int ret; + struct gh_hcall_resp _resp = {0}; + + ret = _gh_hcall(0x6052, + (struct gh_hcall_args){ capid, queue_sel, 0}, + &_resp); + + if (!ret && queue_info) { + queue_info->queue_num = _resp.resp1; + queue_info->queue_ready = _resp.resp2; + queue_info->queue_desc = _resp.resp3; + queue_info->queue_driver = _resp.resp4; + queue_info->queue_device = _resp.resp5; + } + + return ret; +} + +static inline int +gh_hcall_virtio_mmio_backend_get_event(gh_capid_t capid, + u64 *event_data, u64 *event) +{ + int ret; + struct gh_hcall_resp _resp = {0}; + + ret = _gh_hcall(0x6053, + (struct gh_hcall_args){ capid, 0}, + &_resp); + + if (!ret && event_data) + *event_data = _resp.resp1; + if (!ret && event) + *event = _resp.resp2; + + return ret; +} + +static inline int +gh_hcall_virtio_mmio_backend_ack_reset(gh_capid_t capid) +{ + struct gh_hcall_resp _resp = {0}; + + return _gh_hcall(0x6054, + (struct gh_hcall_args){ capid, 0}, + &_resp); +} + +#endif diff --git a/include/trace/events/gh_virtio_backend.h b/include/trace/events/gh_virtio_backend.h new file mode 100644 index 000000000000..7f50ca1e5061 --- /dev/null +++ b/include/trace/events/gh_virtio_backend.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM gh_virtio_backend + +#if !defined(_TRACE_GH_VIRTIO_BACKEND_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_GH_VIRTIO_BACKEND_H + +#include + +TRACE_EVENT(gh_virtio_backend_irq_inj, + + TP_PROTO(int label, int rc), + + TP_ARGS(label, rc), + + TP_STRUCT__entry( + __field(int, label) + __field(int, rc) + ), + + TP_fast_assign( + __entry->label = label; + __entry->rc = rc; + ), + + TP_printk("device %d inj_irq rc %d", __entry->label, __entry->rc) +); + +TRACE_EVENT(gh_virtio_backend_queue_notify, + + TP_PROTO(int label, int qno), + + TP_ARGS(label, qno), + + TP_STRUCT__entry( + __field(int, label) + __field(int, qno) + ), + + TP_fast_assign( + __entry->label = label; + __entry->qno = qno; + ), + + TP_printk("device %d queue_notify on %d", __entry->label, __entry->qno) +); + +TRACE_EVENT(gh_virtio_backend_wait_event, + + TP_PROTO(int label, int cur_event, int org_event, int cur_event_data, int org_event_data), + + TP_ARGS(label, cur_event, org_event, cur_event_data, org_event_data), + + TP_STRUCT__entry( + __field(int, label) + __field(int, cur_event) + __field(int, org_event) + __field(int, cur_event_data) + __field(int, org_event_data) + ), + + TP_fast_assign( + __entry->label = label; + __entry->cur_event = cur_event; + __entry->cur_event_data = cur_event_data; + __entry->org_event = org_event; + __entry->org_event_data = org_event_data; + ), + + TP_printk("device %d cur_evt/org_evt %x/%x, cur_evt_data/org_evt_data %x/%x", + __entry->label, __entry->cur_event, __entry->org_event, + __entry->cur_event_data, __entry->org_event_data) +); + +TRACE_EVENT(gh_virtio_backend_irq, + + TP_PROTO(int label, int event, int event_data, int rc), + + TP_ARGS(label, event, event_data, rc), + + TP_STRUCT__entry( + __field(int, label) + __field(int, event) + __field(int, event_data) + __field(int, rc) + ), + + TP_fast_assign( + __entry->label = label; + __entry->event = event; + __entry->event_data = event_data; + __entry->rc = rc; + ), + + TP_printk("device %d irq (rc %d) event %x event_data %x", + __entry->label, __entry->rc, __entry->event, __entry->event_data) +); + + +#endif /* _TRACE_GH_VIRTIO_BACKEND_H */ + +/* This part must be outside protection */ +#include diff --git a/include/trace/events/gh_virtio_frontend.h b/include/trace/events/gh_virtio_frontend.h new file mode 100644 index 000000000000..0e48e124220a --- /dev/null +++ b/include/trace/events/gh_virtio_frontend.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM gh_virtio_frontend + +#if !defined(_TRACE_GH_VIRTIO_FRONTEND_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_GH_VIRTIO_FRONTEND_H + +#include + +TRACE_EVENT(virtio_mmio_vm_notify, + + TP_PROTO(unsigned int dev_index, unsigned int qindex), + + TP_ARGS(dev_index, qindex), + + TP_STRUCT__entry( + __field(unsigned int, dev_index) + __field(unsigned int, qindex) + ), + + TP_fast_assign( + __entry->dev_index = dev_index; + __entry->qindex = qindex; + ), + + TP_printk("virtio%u qindex %u", __entry->dev_index, __entry->qindex) +); + +TRACE_EVENT(virtio_mmio_vm_interrupt, + + TP_PROTO(unsigned int dev_index, unsigned int status), + + TP_ARGS(dev_index, status), + + TP_STRUCT__entry( + __field(unsigned int, dev_index) + __field(unsigned int, status) + ), + + TP_fast_assign( + __entry->dev_index = dev_index; + __entry->status = status; + ), + + TP_printk("virtio%u status %x", __entry->dev_index, __entry->status) +); + +TRACE_EVENT(virtio_vring_split_add, + + TP_PROTO(unsigned int dev_index, int qhead, unsigned int avail_idx_shadow, + unsigned int descs_used, unsigned int num_free), + + TP_ARGS(dev_index, qhead, avail_idx_shadow, descs_used, num_free), + + TP_STRUCT__entry( + __field(unsigned int, dev_index) + __field(unsigned int, qhead) + __field(unsigned int, avail_idx_shadow) + __field(unsigned int, descs_used) + __field(unsigned int, num_free) + ), + + TP_fast_assign( + __entry->dev_index = dev_index; + __entry->qhead = qhead; + __entry->avail_idx_shadow = avail_idx_shadow; + __entry->descs_used = descs_used; + __entry->num_free = num_free; + ), + + TP_printk("virtio%u qhead %u avail_idx_shadow %u descs_used %u num_free %u", + __entry->dev_index, __entry->qhead, __entry->avail_idx_shadow, + __entry->descs_used, __entry->num_free) +); + +TRACE_EVENT(virtio_detach_buf, + + TP_PROTO(unsigned int dev_index, unsigned int free_head, unsigned int num_free), + + TP_ARGS(dev_index, free_head, num_free), + + TP_STRUCT__entry( + __field(unsigned int, dev_index) + __field(unsigned int, free_head) + __field(unsigned int, num_free) + ), + + TP_fast_assign( + __entry->dev_index = dev_index; + __entry->free_head = free_head; + __entry->num_free = num_free; + ), + + TP_printk("virtio%u free_head %u num_free %u", __entry->dev_index, + __entry->free_head, __entry->num_free) +); + +TRACE_EVENT(virtio_get_buf_ctx_split, + + TP_PROTO(unsigned int dev_index, unsigned int last_used_idx, unsigned int ring_used_idx), + + TP_ARGS(dev_index, last_used_idx, ring_used_idx), + + TP_STRUCT__entry( + __field(unsigned int, dev_index) + __field(unsigned int, last_used_idx) + __field(unsigned int, ring_used_idx) + ), + + TP_fast_assign( + __entry->dev_index = dev_index; + __entry->last_used_idx = last_used_idx; + __entry->ring_used_idx = ring_used_idx; + ), + + TP_printk("virtio%u last_used_idx %u ring_used_idx %u", __entry->dev_index, + __entry->last_used_idx, __entry->ring_used_idx) +); + + +TRACE_EVENT(virtio_block_done, + + TP_PROTO(unsigned int dev_index, unsigned int req_op, unsigned int sector), + + TP_ARGS(dev_index, req_op, sector), + + TP_STRUCT__entry( + __field(unsigned int, dev_index) + __field(unsigned int, req_op) + __field(unsigned int, sector) + ), + + TP_fast_assign( + __entry->dev_index = dev_index; + __entry->req_op = req_op; + __entry->sector = sector; + ), + + TP_printk("virtio%u req_op %u sector %u", __entry->dev_index, + __entry->req_op, __entry->sector) +); + +TRACE_EVENT(virtio_block_submit, + + TP_PROTO(unsigned int dev_index, unsigned int type, unsigned int sector, + unsigned int ioprio, int err, unsigned int num), + + TP_ARGS(dev_index, type, sector, ioprio, err, num), + + TP_STRUCT__entry( + __field(unsigned int, dev_index) + __field(unsigned int, type) + __field(unsigned int, sector) + __field(unsigned int, ioprio) + __field(int, err) + __field(unsigned int, num) + ), + + TP_fast_assign( + __entry->dev_index = dev_index; + __entry->type = type; + __entry->sector = sector; + __entry->ioprio = ioprio; + __entry->err = err; + __entry->num = num; + ), + + TP_printk("virtio%u type %x sector %u ioprio %u num %d err %d", + __entry->dev_index, __entry->type, __entry->sector, + __entry->ioprio, __entry->num, __entry->err) +); + +#endif /* _TRACE_GH_VIRTIO_FRONTEND_H */ + +/* This part must be outside protection */ +#include diff --git a/include/uapi/linux/gh_virtio_backend.h b/include/uapi/linux/gh_virtio_backend.h new file mode 100644 index 000000000000..616953d22dfb --- /dev/null +++ b/include/uapi/linux/gh_virtio_backend.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _UAPI_LINUX_VIRTIO_BACKEND_H +#define _UAPI_LINUX_VIRTIO_BACKEND_H + +#include + +#define VIRTIO_BE_IOC_MAGIC 0xBC + +#define VBE_ASSIGN_IOEVENTFD 1 +#define VBE_DEASSIGN_IOEVENTFD 2 + +#define VBE_ASSIGN_IRQFD 1 +#define VBE_DEASSIGN_IRQFD 2 + +#define EVENT_NEW_BUFFER 1 +#define EVENT_RESET_RQST 2 +#define EVENT_INTERRUPT_ACK 4 +#define EVENT_DRIVER_OK 8 +#define EVENT_DRIVER_FAILED 0x10 +#define EVENT_MODULE_EXIT 0x20 +#define EVENT_VM_EXIT 0x40 +#define EVENT_APP_EXIT 0x100 + +/* + * IOCTLs supported by virtio backend driver + */ +#define GH_GET_SHARED_MEMORY_SIZE _IOR(VIRTIO_BE_IOC_MAGIC, 1, __u64) +#define GH_IOEVENTFD _IOW(VIRTIO_BE_IOC_MAGIC, 2, \ + struct virtio_eventfd) +#define GH_IRQFD _IOW(VIRTIO_BE_IOC_MAGIC, 3, \ + struct virtio_irqfd) +#define GH_WAIT_FOR_EVENT _IOWR(VIRTIO_BE_IOC_MAGIC, 4, \ + struct virtio_event) +#define GH_SET_DEVICE_FEATURES _IOW(VIRTIO_BE_IOC_MAGIC, 5, \ + struct virtio_dev_features) +#define GH_SET_QUEUE_NUM_MAX _IOW(VIRTIO_BE_IOC_MAGIC, 6, \ + struct virtio_queue_max) +#define GH_SET_DEVICE_CONFIG_DATA _IOW(VIRTIO_BE_IOC_MAGIC, 7, \ + struct virtio_config_data) +#define GH_GET_DRIVER_CONFIG_DATA _IOWR(VIRTIO_BE_IOC_MAGIC, 8, \ + struct virtio_config_data) +#define GH_GET_QUEUE_INFO _IOWR(VIRTIO_BE_IOC_MAGIC, 9, \ + struct virtio_queue_info) +#define GH_GET_DRIVER_FEATURES _IOWR(VIRTIO_BE_IOC_MAGIC, 10, \ + struct virtio_driver_features) +#define GH_ACK_DRIVER_OK _IOWR(VIRTIO_BE_IOC_MAGIC, 11, __u32) +#define GH_SET_APP_READY _IO(VIRTIO_BE_IOC_MAGIC, 12) +#define GH_ACK_RESET _IOW(VIRTIO_BE_IOC_MAGIC, 13, struct virtio_ack_reset) + +struct virtio_ack_reset { + __u32 label; + __u32 reserved; +}; + +struct virtio_driver_features { + __u32 label; + __u32 reserved; + __u32 features_sel; + __u32 features; +}; + +struct virtio_queue_info { + __u32 label; + __u32 queue_sel; + __u32 queue_num; + __u32 queue_ready; + __u64 queue_desc; + __u64 queue_driver; + __u64 queue_device; +}; + +struct virtio_config_data { + __u32 label; + __u32 config_size; + __u64 config_data; +}; + +struct virtio_dev_features { + __u32 label; + __u32 reserved; + __u32 features_sel; + __u32 features; +}; + +struct virtio_queue_max { + __u32 label; + __u32 reserved; + __u32 queue_sel; + __u32 queue_num_max; +}; + +struct virtio_event { + __u32 label; + __u32 event; + __u32 event_data; + __u32 reserved; +}; + +struct virtio_eventfd { + __u32 label; + __u32 flags; + __u32 queue_num; + __s32 fd; +}; + +struct virtio_irqfd { + __u32 label; + __u32 flags; + __s32 fd; + __u32 reserved; +}; + +#endif /* _UAPI_LINUX_VIRTIO_BACKEND_H */