diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 447ef2b16364c0de83a859845c29748d0092c3dc..ba180e14a257c8986c664b8e481db3017cac652a 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2054,6 +2054,23 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) return bus->devices[devfn]; } +PCIDevice *pci_find_device_by_bdf(uint16_t bdf) +{ + PCIHostState *host_bridge; + PCIDevice *dev = NULL; + int bus = PCI_BUS_NUM(bdf); + uint8_t devfn = PCI_BDF_TO_DEVFN(bdf); + + QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { + dev = pci_find_device(host_bridge->bus, bus, devfn); + if (dev) { + return dev; + } + } + + return NULL; +} + #define ONBOARD_INDEX_MAX (16 * 1024 - 1) static void pci_qdev_realize(DeviceState *qdev, Error **errp) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 7322c8be638da7177e53b6e027e6a253c49e2b01..9814e064451846eb51ef68795ae5ac83cc1f1482 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1211,6 +1211,28 @@ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) memory_region_transaction_commit(); } +bool get_vfio_dev_bdf(uint16_t guest_dev_bdf, uint16_t *host_dev_bdf) +{ + PCIDevice *pdev = NULL; + VFIOPCIDevice *vdev; + + pdev = pci_find_device_by_bdf(guest_dev_bdf); + if (!pdev) { + return false; + } + + if (!object_dynamic_cast(OBJECT(pdev), TYPE_VFIO_PCI)) { + /* If the pci dev is not passthrough via vfio, it couldn't be realm */ + return false; + } + + vdev = VFIO_PCI(pdev); + *host_dev_bdf = vdev->host.bus << 8 | + PCI_DEVFN(vdev->host.slot, vdev->host.function); + + return true; +} + /* * PCI config space */ diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 6e64a2654e690af11b72710530a41135b726e96f..64a9bd0426c0c94787ab969b5c77238eda91f5bd 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -236,4 +236,6 @@ void vfio_display_finalize(VFIOPCIDevice *vdev); extern const VMStateDescription vfio_display_vmstate; +bool get_vfio_dev_bdf(uint16_t guest_dev_bdf, uint16_t *host_dev_bdf); + #endif /* HW_VFIO_VFIO_PCI_H */ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 0dfe274c33f29c3f327d1f4a68a5fc77acbd8512..8ba492fe14dcb6c982d6a5d75177f1f706d2a89d 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -684,4 +684,5 @@ static inline void pci_irq_deassert(PCIDevice *pci_dev) MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); void pci_set_power(PCIDevice *pci_dev, bool state); +PCIDevice *pci_find_device_by_bdf(uint16_t bdf); #endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 96bc60475e84fa21accd0c2ce517af237980af6b..aa9b9665985f66428dbaa2ba9a2d241ab98a8636 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -267,6 +267,7 @@ struct kvm_xen_exit { #define KVM_EXIT_RISCV_CSR 36 #define KVM_EXIT_NOTIFY 37 #define KVM_EXIT_LOONGARCH_IOCSR 38 +#define KVM_EXIT_ARM_RME_DEV 39 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -517,6 +518,10 @@ struct kvm_run { #define KVM_NOTIFY_CONTEXT_INVALID (1 << 0) __u32 flags; } notify; + /* KVM_EXIT_ARM_RME_DEV */ + struct { + __u16 guest_dev_bdf; + } rme_dev; /* Fix the size of the union. */ char padding[256]; }; @@ -2469,4 +2474,14 @@ struct kvm_arm_rmm_psci_complete { __u32 padding[3]; }; +/* Available with KVM_CAP_ARM_RME, only for VMs with KVM_VM_TYPE_ARM_REALM */ +struct kvm_arm_rme_dev_validate { + __u16 dev_bdf; /* pci dev bdf in the host */ + __u16 guest_dev_bdf; /* pci dev bdf in the realm */ + bool vfio_dev; /* True if the host dev bdf refers to a VFIO dev */ + __u32 reserved[6]; +}; + +#define KVM_ARM_VCPU_RME_DEV_VALIDATE _IOW(KVMIO, 0xd3, struct kvm_arm_rme_dev_validate) + #endif /* __LINUX_KVM_H */ diff --git a/target/arm/kvm-rme.c b/target/arm/kvm-rme.c index 0a16b837d2666fa8675f39ac9a58eb211002ff86..85114c40be3b240975b5e3d64844cc4414733218 100644 --- a/target/arm/kvm-rme.c +++ b/target/arm/kvm-rme.c @@ -759,6 +759,34 @@ static void rme_set_ccal_enable(Object *obj, bool value, Error **errp) guest->ccal_enable = value; } +int kvm_arm_handle_rme_dev(CPUState *cs, uint16_t guest_dev_bdf) +{ + ARMCPU *cpu = ARM_CPU(cs); + uint16_t host_dev_bdf = 0; + bool vfio_dev; + int ret; + + if (!cpu->kvm_rme) { + return -EINVAL; + } + + /* Host dev bdf is valid if the dev is passthrough via VFIO */ + vfio_dev = get_vfio_dev_bdf(guest_dev_bdf, &host_dev_bdf); + struct kvm_arm_rme_dev_validate dev_args = { + .dev_bdf = host_dev_bdf, + .guest_dev_bdf = guest_dev_bdf, + .vfio_dev = vfio_dev, + }; + + ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_RME_DEV_VALIDATE, &dev_args); + if (ret) { + error_report("RME: Failed to validate rdev %#x, %#x, err %s", + guest_dev_bdf, host_dev_bdf, strerror(-ret)); + return -EIO; + } + return 0; +} + static void rme_guest_class_init(ObjectClass *oc, void *data) { object_class_property_add_str(oc, "personalization-value", rme_get_rpv, diff --git a/target/arm/kvm.c b/target/arm/kvm.c index f45783a9dafe76c81e3411fcf35d7b455db3d1e4..2c3439211ef8a2e917c9c5612d333aab8eed8f36 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -1151,6 +1151,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_HYPERCALL: ret = kvm_arm_handle_hypercall(cs, run); break; + case KVM_EXIT_ARM_RME_DEV: + ret = kvm_arm_handle_rme_dev(cs, run->rme_dev.guest_dev_bdf); + break; default: qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", __func__, run->exit_reason); diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 8e9b2039c4dcad02c88ba1b6807b0c83aadb4d4d..78e8e40ee6f2a3af9edf671cb85f8288b5447d9f 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -639,4 +639,15 @@ void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr); */ bool kvm_arm_verify_ext_dabt_pending(CPUState *cs); +/** + * kvm_arm_handle_rme_dev: + * @cs: CPUState + * @guest_dev_bdf: bdf of a pci dev in realm + * + * Find the corresponding host pci dev of the pci dev in realm, request host to + * validate whether the dev is realm. + * + * Returns: 0 if the host pci dev if found, and the request is sent. + */ +int kvm_arm_handle_rme_dev(CPUState *cs, uint16_t guest_dev_bdf); #endif