From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH] VHM: add passthrough device support add following ioctl in vhm_dev to support device passthrough - assign, deassign pass-through device ACRN_ASSIGN_PTDEV ACRN_DEASSIGN_PTDEV - set, reset pass-through device intr info ACRN_SET_PTDEV_INTR_INFO ACRN_RESET_PTDEV_INTR_INFO reuse exist ioctl to support device passthrough - BAR mapping ACRN_IOC_SET_MEMSEG - MSI support ACRN_VM_PCI_MSIX_REMAP Change-Id: I94bbee48e8de1faf70804061c65c2e2855e6bf0f Tracked-On: 218445 Signed-off-by: Gao, Shiqing Signed-off-by: Binbin Wu Signed-off-by: Edwin Zhai Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 25 +++++ drivers/vhm/vhm_hypercall.c | 175 +++++++++++++++++++++++++++++ include/linux/vhm/acrn_common.h | 43 +++++++ include/linux/vhm/acrn_hv_defs.h | 8 ++ include/linux/vhm/vhm_hypercall.h | 8 ++ include/linux/vhm/vhm_ioctl_defs.h | 9 ++ 6 files changed, 268 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 97f7c466e11d..97c20d82154f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -267,6 +267,31 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_ASSIGN_PTDEV: { + ret = vhm_assign_ptdev(vm, ioctl_param); + break; + } + + case IC_DEASSIGN_PTDEV: { + ret = vhm_deassign_ptdev(vm, ioctl_param); + break; + } + + case IC_SET_PTDEV_INTR_INFO: { + ret = vhm_set_ptdev_intr_info(vm, ioctl_param); + break; + } + + case IC_RESET_PTDEV_INTR_INFO: { + ret = vhm_reset_ptdev_intr_info(vm, ioctl_param); + break; + } + + case IC_VM_PCI_MSIX_REMAP: { + ret = vhm_remap_pci_msix(vm, ioctl_param); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 384b86e60c9c..0f3f6c1c5f4c 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -50,14 +50,30 @@ */ #include #include +#include #include #include +/* max num of pass-through devices using msix */ +#define MAX_ENTRY 3 + +struct table_iomems { + /* device's virtual BDF */ + unsigned short virt_bdf; + /* virtual base address of MSI-X table in memory space after ioremap */ + unsigned long mmap_addr; +} tables[MAX_ENTRY]; + inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) { return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); } +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix) +{ + return acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vmid, msix); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); @@ -211,3 +227,162 @@ inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param) return ret; } + +inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = acrn_hypercall2(HC_ASSIGN_PTDEV, vm->vmid, + virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to assign ptdev!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = acrn_hypercall2(HC_DEASSIGN_PTDEV, vm->vmid, + virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to deassign ptdev!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_SET_PTDEV_INTR_INFO, vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to set intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + + return ret; +} + +inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_RESET_PTDEV_INTR_INFO, vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to reset intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + + return ret; +} + +inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_vm_pci_msix_remap msix_remap; + + if (copy_from_user(&msix_remap, + (void *)ioctl_param, sizeof(msix_remap))) + return -EFAULT; + + ret = acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vm->vmid, + virt_to_phys(&msix_remap)); + + if (copy_to_user((void *)ioctl_param, + &msix_remap, sizeof(msix_remap))) + return -EFAULT; + + if (msix_remap.msix) { + void __iomem *msix_entry; + int i; + + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf == msix_remap.virt_bdf) + break; + } + + if (!tables[i].mmap_addr) + return -EFAULT; + + msix_entry = (void *)(tables[i].mmap_addr + + msix_remap.msix_entry_index * + PCI_MSIX_ENTRY_SIZE); + + /* mask the entry when setup */ + writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + + /* setup the msi entry */ + writel((uint32_t)msix_remap.msi_addr, + msix_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + writel((uint32_t)(msix_remap.msi_addr >> 32), + msix_entry + PCI_MSIX_ENTRY_UPPER_ADDR); + writel(msix_remap.msi_data, + msix_entry + PCI_MSIX_ENTRY_DATA); + + /* unmask the entry */ + writel(msix_remap.vector_ctl & + PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + } + + return ret; +} diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index f0567aa0f1dd..5723e5e2d537 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -99,6 +99,15 @@ enum request_direction { DIRECTION_MAX, } __attribute__((aligned(4))); +/* + * IRQ type for ptdev + */ +enum irq_type { + IRQ_INTX, + IRQ_MSI, + IRQ_MSIX, +} __attribute__((aligned(4))); + struct msr_request { enum request_direction direction; long index; @@ -216,4 +225,38 @@ struct vm_gpa2hpa { unsigned long hpa; /* OUT: -1 means invalid gpa */ } __attribute__((aligned(8))); +struct acrn_ptdev_irq { + enum irq_type type; + unsigned short virt_bdf; /* IN: Device virtual BDF# */ + unsigned short phys_bdf; /* IN: Device physical BDF# */ + union { + struct { + int virt_pin; /* IN: virtual IOAPIC pin */ + int phys_pin; /* IN: physical IOAPIC pin */ + bool pic_pin; /* IN: pin from PIC? */ + } intx; + struct { + int vector_cnt; /* IN: vector count of MSI/MSIX */ + + /* IN: physcial address of MSI-X table */ + unsigned long table_paddr; + + /* IN: size of MSI-X table (round up to 4K) */ + int table_size; + } msix; + }; +} __attribute__((aligned(8))); + +struct acrn_vm_pci_msix_remap { + unsigned short virt_bdf; /* IN: Device virtual BDF# */ + unsigned short phys_bdf; /* IN: Device physical BDF# */ + unsigned short msi_ctl; /* IN: PCI MSI/x cap control data */ + unsigned long msi_addr; /* IN/OUT: msi address to fix */ + unsigned int msi_data; /* IN/OUT: msi data to fix */ + int msix; /* IN: 0 - MSI, 1 - MSI-X */ + int msix_entry_index; /* IN: MSI-X the entry table index */ + /* IN: Vector Control for MSI-X Entry, field defined in MSIX spec */ + unsigned int vector_ctl; +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d527a8fa8435..3e43da56813d 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -92,6 +92,14 @@ #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) +/* PCI assignment*/ +#define HC_ID_PCI_BASE 0x400UL +#define HC_ASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x00) +#define HC_DEASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x01) +#define HC_VM_PCI_MSIX_REMAP _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x02) +#define HC_SET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03) +#define HC_RESET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) #define ACRN_INVALID_HPA (-1UL) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index f1ed9a07e708..ce579e3734ff 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -140,6 +140,7 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, } inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); @@ -153,5 +154,12 @@ inline long vhm_query_vm_state(struct vhm_vm *vm); inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param); +inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param); +inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param); #endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 3be6aca40844..8d03d38b788d 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -79,11 +79,20 @@ #define IC_ATTACH_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x03) #define IC_DESTROY_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x04) + /* Guest memory management */ #define IC_ID_MEM_BASE 0x300UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) +/* PCI assignment*/ +#define IC_ID_PCI_BASE 0x400UL +#define IC_ASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x00) +#define IC_DEASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x01) +#define IC_VM_PCI_MSIX_REMAP _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x02) +#define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) +#define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) + #define SPECNAMELEN 63 enum { -- https://clearlinux.org