hv: vPCI: add assign/deassign PCI device HC APIs

Add assign/deassign PCI device hypercall APIs to assign a PCI device from SOS to
post-launched VM or deassign a PCI device from post-launched VM to SOS. This patch
is prepared for spliting passthrough PCI device from DM to HV.
The old assign/deassign ptdev APIs will be discarded.

Tracked-On: #4371
Signed-off-by: Li Fei1 <fei1.li@intel.com>
This commit is contained in:
Li Fei1 2020-01-16 22:09:33 +08:00 committed by wenlingz
parent 2ca01206f3
commit fe3182ea05
8 changed files with 260 additions and 8 deletions

View File

@ -181,6 +181,20 @@ static int32_t dispatch_sos_hypercall(const struct acrn_vcpu *vcpu)
}
break;
case HC_ASSIGN_PCIDEV:
/* param1: relative vmid to sos, vm_id: absolute vmid */
if (vmid_is_valid) {
ret = hcall_assign_pcidev(sos_vm, vm_id, param2);
}
break;
case HC_DEASSIGN_PCIDEV:
/* param1: relative vmid to sos, vm_id: absolute vmid */
if (vmid_is_valid) {
ret = hcall_deassign_pcidev(sos_vm, vm_id, param2);
}
break;
case HC_SET_PTDEV_INTR_INFO:
/* param1: relative vmid to sos, vm_id: absolute vmid */
if (vmid_is_valid) {

View File

@ -1046,7 +1046,7 @@ static void dmar_resume(struct dmar_drhd_rt *dmar_unit)
dmar_enable_intr_remapping(dmar_unit);
}
static int32_t add_iommu_device(struct iommu_domain *domain, uint8_t bus, uint8_t devfun)
static int32_t iommu_attach_device(struct iommu_domain *domain, uint8_t bus, uint8_t devfun)
{
struct dmar_drhd_rt *dmar_unit;
struct dmar_entry *root_table;
@ -1161,7 +1161,7 @@ static int32_t add_iommu_device(struct iommu_domain *domain, uint8_t bus, uint8_
return ret;
}
static int32_t remove_iommu_device(const struct iommu_domain *domain, uint8_t bus, uint8_t devfun)
static int32_t iommu_detach_device(const struct iommu_domain *domain, uint8_t bus, uint8_t devfun)
{
struct dmar_drhd_rt *dmar_unit;
struct dmar_entry *root_table;
@ -1309,11 +1309,11 @@ int32_t move_pt_device(const struct iommu_domain *from_domain, struct iommu_doma
if (bus_local < CONFIG_IOMMU_BUS_NUM) {
if (from_domain != NULL) {
status = remove_iommu_device(from_domain, bus, devfun);
status = iommu_detach_device(from_domain, bus, devfun);
}
if ((status == 0) && (to_domain != NULL)) {
status = add_iommu_device(to_domain, bus, devfun);
status = iommu_attach_device(to_domain, bus, devfun);
}
} else {
status = -EINVAL;

View File

@ -882,6 +882,66 @@ int32_t hcall_deassign_ptdev(struct acrn_vm *vm, uint16_t vmid, uint64_t param)
return ret;
}
/**
* @brief Assign one PCI dev to a VM.
*
* @param vm Pointer to VM data structure
* @param vmid ID of the VM
* @param param guest physical address. This gpa points to data structure of
* acrn_assign_pcidev including assign PCI device info
*
* @pre Pointer vm shall point to SOS_VM
* @return 0 on success, non-zero on error.
*/
int32_t hcall_assign_pcidev(struct acrn_vm *vm, uint16_t vmid, uint64_t param)
{
int32_t ret = -EINVAL;
struct acrn_assign_pcidev pcidev;
struct acrn_vm *target_vm = get_vm_from_vmid(vmid);
if (!is_poweroff_vm(target_vm) && is_postlaunched_vm(target_vm)) {
if (copy_from_gpa(vm, &pcidev, param, sizeof(pcidev)) != 0) {
pr_err("%s: Unable copy param to vm\n", __func__);
} else {
ret = vpci_assign_pcidev(target_vm, &pcidev);
}
} else {
pr_err("%s, vm[%d] is invalid\n", __func__, vm->vm_id);
}
return ret;
}
/**
* @brief Deassign one PCI dev from a VM.
*
* @param vm Pointer to VM data structure
* @param vmid ID of the VM
* @param param guest physical address. This gpa points to data structure of
* acrn_assign_pcidev including deassign PCI device info
*
* @pre Pointer vm shall point to SOS_VM
* @return 0 on success, non-zero on error.
*/
int32_t hcall_deassign_pcidev(struct acrn_vm *vm, uint16_t vmid, uint64_t param)
{
int32_t ret = -EINVAL;
struct acrn_assign_pcidev pcidev;
struct acrn_vm *target_vm = get_vm_from_vmid(vmid);
if (!is_poweroff_vm(target_vm) && is_postlaunched_vm(target_vm)) {
if (copy_from_gpa(vm, &pcidev, param, sizeof(pcidev)) != 0) {
pr_err("%s: Unable copy param to vm\n", __func__);
} else {
ret = vpci_deassign_pcidev(target_vm, &pcidev);
}
} else {
pr_err("%s, vm[%d] is invalid\n", __func__, vm->vm_id);
}
return ret;
}
/**
* @brief Set interrupt mapping info of ptdev.
*

View File

@ -27,6 +27,7 @@
* $FreeBSD$
*/
#include <errno.h>
#include <vm.h>
#include <vtd.h>
#include <io.h>
@ -301,7 +302,7 @@ void vpci_cleanup(struct acrn_vm *vm)
* @pre vdev->vpci->vm != NULL
* @pre vdev->vpci->vm->iommu != NULL
*/
static void assign_vdev_pt_iommu_domain(const struct pci_vdev *vdev)
static void assign_vdev_pt_iommu_domain(struct pci_vdev *vdev)
{
int32_t ret;
struct acrn_vm *vm = vdev->vpci->vm;
@ -322,7 +323,7 @@ static void assign_vdev_pt_iommu_domain(const struct pci_vdev *vdev)
static void remove_vdev_pt_iommu_domain(const struct pci_vdev *vdev)
{
int32_t ret;
struct acrn_vm *vm = vdev->vpci->vm;
const struct acrn_vm *vm = vdev->vpci->vm;
ret = move_pt_device(vm->iommu, NULL, (uint8_t)vdev->pdev->bdf.bits.b,
(uint8_t)(vdev->pdev->bdf.value & 0xFFU));
@ -464,7 +465,7 @@ static void write_cfg(struct acrn_vpci *vpci, union pci_bdf bdf,
* @pre vpci != NULL
* @pre vpci.pci_vdev_cnt <= CONFIG_MAX_PCI_DEV_NUM
*/
static void vpci_init_vdev(struct acrn_vpci *vpci, struct acrn_vm_pci_dev_config *dev_config)
static struct pci_vdev *vpci_init_vdev(struct acrn_vpci *vpci, struct acrn_vm_pci_dev_config *dev_config)
{
struct pci_vdev *vdev = &vpci->pci_vdevs[vpci->pci_vdev_cnt];
@ -484,6 +485,8 @@ static void vpci_init_vdev(struct acrn_vpci *vpci, struct acrn_vm_pci_dev_config
}
vdev->vdev_ops->init_vdev(vdev);
return vdev;
}
/**
@ -496,7 +499,7 @@ static void vpci_init_vdevs(struct acrn_vm *vm)
const struct acrn_vm_config *vm_config = get_vm_config(vpci->vm->vm_id);
for (idx = 0U; idx < vm_config->pci_dev_num; idx++) {
vpci_init_vdev(vpci, &vm_config->pci_devs[idx]);
(void)vpci_init_vdev(vpci, &vm_config->pci_devs[idx]);
}
}
@ -630,3 +633,112 @@ void vpci_reset_ptdev_intr_info(struct acrn_vm *target_vm, uint16_t vbdf, uint16
}
spinlock_release(&sos_vm->vpci.lock);
}
/**
* @brief assign a PCI device from SOS to target post-launched VM.
*
* @pre tgt_vm != NULL
* @pre pcidev != NULL
*/
int32_t vpci_assign_pcidev(struct acrn_vm *tgt_vm, struct acrn_assign_pcidev *pcidev)
{
int32_t ret = 0;
uint32_t idx;
struct pci_vdev *vdev_in_sos, *vdev;
struct acrn_vpci *vpci;
union pci_bdf bdf;
struct acrn_vm *sos_vm;
bdf.value = pcidev->phys_bdf;
sos_vm = get_sos_vm();
spinlock_obtain(&sos_vm->vpci.lock);
vdev_in_sos = pci_find_vdev(&sos_vm->vpci, bdf);
if ((vdev_in_sos != NULL) && (vdev_in_sos->vpci->vm == sos_vm) && (vdev_in_sos->pdev != NULL)) {
/* ToDo: Each PT device must support one type reset */
if (!vdev_in_sos->pdev->has_pm_reset && !vdev_in_sos->pdev->has_flr &&
!vdev_in_sos->pdev->has_af_flr) {
pr_fatal("%s %x:%x.%x not support FLR or not support PM reset\n",
__func__, bdf.bits.b, bdf.bits.d, bdf.bits.f);
} else {
/* DM will reset this device before assigning it */
pdev_restore_bar(vdev_in_sos->pdev);
}
remove_vdev_pt_iommu_domain(vdev_in_sos);
if (ret == 0) {
vpci = &(tgt_vm->vpci);
vdev_in_sos->vpci = vpci;
spinlock_obtain(&tgt_vm->vpci.lock);
vdev = vpci_init_vdev(vpci, vdev_in_sos->pci_dev_config);
pci_vdev_write_cfg_u8(vdev, PCIR_INTERRUPT_LINE, pcidev->intr_line);
pci_vdev_write_cfg_u8(vdev, PCIR_INTERRUPT_PIN, pcidev->intr_pin);
for (idx = 0U; idx < vdev->nr_bars; idx++) {
pci_vdev_write_bar(vdev, idx, pcidev->bar[idx]);
}
vdev->bdf.value = pcidev->virt_bdf;
spinlock_release(&tgt_vm->vpci.lock);
vdev_in_sos->new_owner = vdev;
}
} else {
pr_fatal("%s, can't find PCI device %x:%x.%x for vm[%d] %x:%x.%x\n", __func__,
pcidev->phys_bdf >> 8U, (pcidev->phys_bdf >> 3U) & 0x1fU, pcidev->phys_bdf & 0x7U,
tgt_vm->vm_id,
pcidev->virt_bdf >> 8U, (pcidev->virt_bdf >> 3U) & 0x1fU, pcidev->virt_bdf & 0x7U);
ret = -ENODEV;
}
spinlock_release(&sos_vm->vpci.lock);
return ret;
}
/**
* @brief deassign a PCI device from target post-launched VM to SOS.
*
* @pre tgt_vm != NULL
* @pre pcidev != NULL
*/
int32_t vpci_deassign_pcidev(struct acrn_vm *tgt_vm, struct acrn_assign_pcidev *pcidev)
{
int32_t ret = 0;
struct pci_vdev *vdev_in_sos, *vdev;
union pci_bdf bdf;
struct acrn_vm *sos_vm;
bdf.value = pcidev->phys_bdf;
sos_vm = get_sos_vm();
spinlock_obtain(&sos_vm->vpci.lock);
vdev_in_sos = pci_find_vdev(&sos_vm->vpci, bdf);
if ((vdev_in_sos != NULL) && (vdev_in_sos->vpci->vm == tgt_vm) && (vdev_in_sos->pdev != NULL)) {
vdev = vdev_in_sos->new_owner;
spinlock_obtain(&tgt_vm->vpci.lock);
if (vdev != NULL) {
ret = move_pt_device(tgt_vm->iommu, sos_vm->iommu, (uint8_t)(pcidev->phys_bdf >> 8U),
(uint8_t)(pcidev->phys_bdf & 0xffU));
if (ret != 0) {
panic("failed to assign iommu device!");
}
deinit_vmsi(vdev);
deinit_vmsix(vdev);
}
spinlock_release(&tgt_vm->vpci.lock);
vdev_in_sos->vpci = &sos_vm->vpci;
vdev_in_sos->new_owner = NULL;
} else {
pr_fatal("%s, can't find PCI device %x:%x.%x for vm[%d] %x:%x.%x\n", __func__,
pcidev->phys_bdf >> 8U, (pcidev->phys_bdf >> 3U) & 0x1fU, pcidev->phys_bdf & 0x7U,
tgt_vm->vm_id,
pcidev->virt_bdf >> 8U, (pcidev->virt_bdf >> 3U) & 0x1fU, pcidev->virt_bdf & 0x7U);
ret = -ENODEV;
}
spinlock_release(&sos_vm->vpci.lock);
return ret;
}

View File

@ -272,6 +272,32 @@ int32_t hcall_assign_ptdev(struct acrn_vm *vm, uint16_t vmid, uint64_t param);
*/
int32_t hcall_deassign_ptdev(struct acrn_vm *vm, uint16_t vmid, uint64_t param);
/**
* @brief Assign one PCI dev to VM.
*
* @param vm Pointer to VM data structure
* @param vmid ID of the VM
* @param param guest physical address. This gpa points to data structure of
* acrn_assign_pcidev including assign PCI device info
*
* @pre Pointer vm shall point to SOS_VM
* @return 0 on success, non-zero on error.
*/
int32_t hcall_assign_pcidev(struct acrn_vm *vm, uint16_t vmid, uint64_t param);
/**
* @brief Deassign one PCI dev to VM.
*
* @param vm Pointer to VM data structure
* @param vmid ID of the VM
* @param param guest physical address. This gpa points to data structure of
* acrn_assign_pcidev including deassign PCI device info
*
* @pre Pointer vm shall point to SOS_VM
* @return 0 on success, non-zero on error.
*/
int32_t hcall_deassign_pcidev(struct acrn_vm *vm, uint16_t vmid, uint64_t param);
/**
* @brief Set interrupt mapping info of ptdev.
*

View File

@ -134,5 +134,8 @@ void vpci_cleanup(struct acrn_vm *vm);
struct pci_vdev *pci_find_vdev(struct acrn_vpci *vpci, union pci_bdf vbdf);
void vpci_set_ptdev_intr_info(struct acrn_vm *target_vm, uint16_t vbdf, uint16_t pbdf);
void vpci_reset_ptdev_intr_info(struct acrn_vm *target_vm, uint16_t vbdf, uint16_t pbdf);
struct acrn_assign_pcidev;
int32_t vpci_assign_pcidev(struct acrn_vm *tgt_vm, struct acrn_assign_pcidev *pcidev);
int32_t vpci_deassign_pcidev(struct acrn_vm *tgt_vm, struct acrn_assign_pcidev *pcidev);
#endif /* VPCI_H_ */

View File

@ -90,6 +90,8 @@
#define PCIR_CAP_PTR_CARDBUS 0x14U
#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL)
#define PCIR_INTERRUPT_LINE 0x3cU
#define PCIR_INTERRUPT_PIN 0x3dU
/* config registers for header type 1 (PCI-to-PCI bridge) devices */
#define PCIR_PRIBUS_1 0x18U

View File

@ -64,6 +64,8 @@
#define HC_VM_PCI_MSIX_REMAP BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x02UL)
#define HC_SET_PTDEV_INTR_INFO BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03UL)
#define HC_RESET_PTDEV_INTR_INFO BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04UL)
#define HC_ASSIGN_PCIDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x05UL)
#define HC_DEASSIGN_PCIDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x06UL)
/* DEBUG */
#define HC_ID_DBG_BASE 0x60UL
@ -271,6 +273,39 @@ struct hc_ptdev_irq {
} is; /* irq source */
} __aligned(8);
/**
* @brief Info to assign or deassign PCI for a VM
*
* the parameter for HC_ASSIGN_PCIDEV or HC_DEASSIGN_PCIDEV hypercall
*/
struct acrn_assign_pcidev {
/** reversed for externed compatibility */
uint32_t rsvd1;
/** virtual BDF# of the pass-through PCI device */
uint16_t virt_bdf;
/** physical BDF# of the pass-through PCI device */
uint16_t phys_bdf;
/** the PCI Interrupt Line, initialized by ACRN-DM, which is RO and
* ideally not used for pass-through MSI/MSI-x devices.
*/
uint8_t intr_line;
/** the PCI Interrupt Pin, initialized by ACRN-DM, which is RO and
* ideally not used for pass-through MSI/MSI-x devices.
*/
uint8_t intr_pin;
/** the base address of the PCI BAR, initialized by ACRN-DM. */
uint32_t bar[6];
/** reserved for extension */
uint32_t rsvd2[6];
} __attribute__((aligned(8)));
/**
* Hypervisor api version info, return it for HC_GET_API_VERSION hypercall
*/