mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-21 13:08:42 +00:00
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:
parent
2ca01206f3
commit
fe3182ea05
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user