From: Shuo Liu <shuo.a....@intel.com>

PCI device passthrough enables an OS in a virtual machine to directly
access a PCI device in the host. It promises almost the native
performance, which is required in performance-critical scenarios of
ACRN.

HSM provides the following ioctls:
 - Assign - ACRN_IOCTL_ASSIGN_PCIDEV
   Pass data struct acrn_pcidev from userspace to the hypervisor, and
   inform the hypervisor to assign a PCI device to a User VM.

 - De-assign - ACRN_IOCTL_DEASSIGN_PCIDEV
   Pass data struct acrn_pcidev from userspace to the hypervisor, and
   inform the hypervisor to de-assign a PCI device from a User VM.

 - Set a interrupt of a passthrough device - ACRN_IOCTL_SET_PTDEV_INTR
   Pass data struct acrn_ptdev_irq from userspace to the hypervisor,
   and inform the hypervisor to map a INTx interrupt of passthrough
   device of User VM.

 - Reset passthrough device interrupt - ACRN_IOCTL_RESET_PTDEV_INTR
   Pass data struct acrn_ptdev_irq from userspace to the hypervisor,
   and inform the hypervisor to unmap a INTx interrupt of passthrough
   device of User VM.

Signed-off-by: Shuo Liu <shuo.a....@intel.com>
Reviewed-by: Zhi Wang <zhi.a.w...@intel.com>
Reviewed-by: Reinette Chatre <reinette.cha...@intel.com>
Cc: Zhi Wang <zhi.a.w...@intel.com>
Cc: Zhenyu Wang <zhen...@linux.intel.com>
Cc: Yu Wang <yu1.w...@intel.com>
Cc: Reinette Chatre <reinette.cha...@intel.com>
---
 drivers/virt/acrn/hsm.c       | 46 +++++++++++++++++++++++++
 drivers/virt/acrn/hypercall.h | 54 ++++++++++++++++++++++++++++++
 include/uapi/linux/acrn.h     | 63 +++++++++++++++++++++++++++++++++++
 3 files changed, 163 insertions(+)

diff --git a/drivers/virt/acrn/hsm.c b/drivers/virt/acrn/hsm.c
index 3c7bea54e476..ab2a052900e8 100644
--- a/drivers/virt/acrn/hsm.c
+++ b/drivers/virt/acrn/hsm.c
@@ -50,7 +50,9 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int 
cmd,
        struct acrn_vm_creation *vm_param;
        struct acrn_vcpu_regs *cpu_regs;
        struct acrn_ioreq_notify notify;
+       struct acrn_ptdev_irq *irq_info;
        struct acrn_vm_memmap memmap;
+       struct acrn_pcidev *pcidev;
        int ret = 0;
 
        if (cmd == ACRN_IOCTL_GET_API_VERSION) {
@@ -130,6 +132,50 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int 
cmd,
 
                ret = acrn_vm_memseg_unmap(vm, &memmap);
                break;
+       case ACRN_IOCTL_ASSIGN_PCIDEV:
+               pcidev = memdup_user((void __user *)ioctl_param,
+                                    sizeof(struct acrn_pcidev));
+               if (IS_ERR(pcidev))
+                       return PTR_ERR(pcidev);
+
+               ret = hcall_assign_pcidev(vm->vmid, virt_to_phys(pcidev));
+               if (ret < 0)
+                       pr_err("Failed to assign pci device!\n");
+               kfree(pcidev);
+               break;
+       case ACRN_IOCTL_DEASSIGN_PCIDEV:
+               pcidev = memdup_user((void __user *)ioctl_param,
+                                    sizeof(struct acrn_pcidev));
+               if (IS_ERR(pcidev))
+                       return PTR_ERR(pcidev);
+
+               ret = hcall_deassign_pcidev(vm->vmid, virt_to_phys(pcidev));
+               if (ret < 0)
+                       pr_err("Failed to deassign pci device!\n");
+               kfree(pcidev);
+               break;
+       case ACRN_IOCTL_SET_PTDEV_INTR:
+               irq_info = memdup_user((void __user *)ioctl_param,
+                                      sizeof(struct acrn_ptdev_irq));
+               if (IS_ERR(irq_info))
+                       return PTR_ERR(irq_info);
+
+               ret = hcall_set_ptdev_intr(vm->vmid, virt_to_phys(irq_info));
+               if (ret < 0)
+                       pr_err("Failed to configure intr for ptdev!\n");
+               kfree(irq_info);
+               break;
+       case ACRN_IOCTL_RESET_PTDEV_INTR:
+               irq_info = memdup_user((void __user *)ioctl_param,
+                                      sizeof(struct acrn_ptdev_irq));
+               if (IS_ERR(irq_info))
+                       return PTR_ERR(irq_info);
+
+               ret = hcall_reset_ptdev_intr(vm->vmid, virt_to_phys(irq_info));
+               if (ret < 0)
+                       pr_err("Failed to reset intr for ptdev!\n");
+               kfree(irq_info);
+               break;
        case ACRN_IOCTL_CREATE_IOREQ_CLIENT:
                if (vm->default_client)
                        return -EEXIST;
diff --git a/drivers/virt/acrn/hypercall.h b/drivers/virt/acrn/hypercall.h
index d85dbcdb9f00..41409df943a2 100644
--- a/drivers/virt/acrn/hypercall.h
+++ b/drivers/virt/acrn/hypercall.h
@@ -31,6 +31,12 @@
 #define HC_ID_MEM_BASE                 0x40UL
 #define HC_VM_SET_MEMORY_REGIONS       _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02)
 
+#define HC_ID_PCI_BASE                 0x50UL
+#define HC_SET_PTDEV_INTR              _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03)
+#define HC_RESET_PTDEV_INTR            _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04)
+#define HC_ASSIGN_PCIDEV               _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x05)
+#define HC_DEASSIGN_PCIDEV             _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x06)
+
 /**
  * hcall_get_api_version() - Get API version from hypervisor
  * @api_version:       Service VM GPA of version info
@@ -144,4 +150,52 @@ static inline long hcall_set_memory_regions(u64 regions_pa)
        return acrn_hypercall1(HC_VM_SET_MEMORY_REGIONS, regions_pa);
 }
 
+/**
+ * hcall_assign_pcidev() - Assign a PCI device to a User VM
+ * @vmid:      User VM ID
+ * @addr:      Service VM GPA of the &struct acrn_pcidev
+ *
+ * Return: 0 on success, <0 on failure
+ */
+static inline long hcall_assign_pcidev(u64 vmid, u64 addr)
+{
+       return acrn_hypercall2(HC_ASSIGN_PCIDEV, vmid, addr);
+}
+
+/**
+ * hcall_deassign_pcidev() - De-assign a PCI device from a User VM
+ * @vmid:      User VM ID
+ * @addr:      Service VM GPA of the &struct acrn_pcidev
+ *
+ * Return: 0 on success, <0 on failure
+ */
+static inline long hcall_deassign_pcidev(u64 vmid, u64 addr)
+{
+       return acrn_hypercall2(HC_DEASSIGN_PCIDEV, vmid, addr);
+}
+
+/**
+ * hcall_set_ptdev_intr() - Configure an interrupt for an assigned PCI device.
+ * @vmid:      User VM ID
+ * @irq:       Service VM GPA of the &struct acrn_ptdev_irq
+ *
+ * Return: 0 on success, <0 on failure
+ */
+static inline long hcall_set_ptdev_intr(u64 vmid, u64 irq)
+{
+       return acrn_hypercall2(HC_SET_PTDEV_INTR, vmid, irq);
+}
+
+/**
+ * hcall_reset_ptdev_intr() - Reset an interrupt for an assigned PCI device.
+ * @vmid:      User VM ID
+ * @irq:       Service VM GPA of the &struct acrn_ptdev_irq
+ *
+ * Return: 0 on success, <0 on failure
+ */
+static inline long hcall_reset_ptdev_intr(u64 vmid, u64 irq)
+{
+       return acrn_hypercall2(HC_RESET_PTDEV_INTR, vmid, irq);
+}
+
 #endif /* __ACRN_HSM_HYPERCALL_H */
diff --git a/include/uapi/linux/acrn.h b/include/uapi/linux/acrn.h
index c8bb71a1cef7..45891da3d8a1 100644
--- a/include/uapi/linux/acrn.h
+++ b/include/uapi/linux/acrn.h
@@ -299,6 +299,60 @@ struct acrn_vm_memmap {
        __u32   attr;
 } __attribute__((aligned(8)));
 
+/* Type of interrupt of a passthrough device */
+#define ACRN_PTDEV_IRQ_INTX    0
+#define ACRN_PTDEV_IRQ_MSI     1
+#define ACRN_PTDEV_IRQ_MSIX    2
+/**
+ * struct acrn_ptdev_irq - Interrupt data of a passthrough device.
+ * @type:              Type (ACRN_PTDEV_IRQ_*)
+ * @virt_bdf:          Virtual Bus/Device/Function
+ * @phys_bdf:          Physical Bus/Device/Function
+ * @intx:              Info of interrupt
+ * @intx.virt_pin:     Virtual IOAPIC pin
+ * @intx.phys_pin:     Physical IOAPIC pin
+ * @intx.is_pic_pin:   Is PIC pin or not
+ *
+ * This structure will be passed to hypervisor directly.
+ */
+struct acrn_ptdev_irq {
+       __u32   type;
+       __u16   virt_bdf;
+       __u16   phys_bdf;
+
+       struct {
+               __u32   virt_pin;
+               __u32   phys_pin;
+               __u32   is_pic_pin;
+       } intx;
+} __attribute__((aligned(8)));
+
+/* Type of PCI device assignment */
+#define ACRN_PTDEV_QUIRK_ASSIGN        (1U << 0)
+
+#define ACRN_PCI_NUM_BARS      6
+/**
+ * struct acrn_pcidev - Info for assigning or de-assigning a PCI device
+ * @type:      Type of the assignment
+ * @virt_bdf:  Virtual Bus/Device/Function
+ * @phys_bdf:  Physical Bus/Device/Function
+ * @intr_line: PCI interrupt line
+ * @intr_pin:  PCI interrupt pin
+ * @bar:       PCI BARs.
+ * @reserved:  Reserved.
+ *
+ * This structure will be passed to hypervisor directly.
+ */
+struct acrn_pcidev {
+       __u32   type;
+       __u16   virt_bdf;
+       __u16   phys_bdf;
+       __u8    intr_line;
+       __u8    intr_pin;
+       __u32   bar[ACRN_PCI_NUM_BARS];
+       __u32   reserved[6];
+} __attribute__((aligned(8)));
+
 /* The ioctl type, documented in ioctl-number.rst */
 #define ACRN_IOCTL_TYPE                        0xA2
 
@@ -337,4 +391,13 @@ struct acrn_vm_memmap {
 #define ACRN_IOCTL_UNSET_MEMSEG                \
        _IOW(ACRN_IOCTL_TYPE, 0x42, struct acrn_vm_memmap)
 
+#define ACRN_IOCTL_SET_PTDEV_INTR      \
+       _IOW(ACRN_IOCTL_TYPE, 0x53, struct acrn_ptdev_irq)
+#define ACRN_IOCTL_RESET_PTDEV_INTR    \
+       _IOW(ACRN_IOCTL_TYPE, 0x54, struct acrn_ptdev_irq)
+#define ACRN_IOCTL_ASSIGN_PCIDEV       \
+       _IOW(ACRN_IOCTL_TYPE, 0x55, struct acrn_pcidev)
+#define ACRN_IOCTL_DEASSIGN_PCIDEV     \
+       _IOW(ACRN_IOCTL_TYPE, 0x56, struct acrn_pcidev)
+
 #endif /* _UAPI_ACRN_H */
-- 
2.28.0

Reply via email to