diff --git a/arch/arm64/kvm/hyp/include/nvhe/iommu.h b/arch/arm64/kvm/hyp/include/nvhe/iommu.h index 6b38dc74dfa2..6980fd7dd252 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/iommu.h +++ b/arch/arm64/kvm/hyp/include/nvhe/iommu.h @@ -48,6 +48,7 @@ int kvm_iommu_snapshot_host_stage2(struct kvm_hyp_iommu_domain *domain); int kvm_iommu_dev_block_dma(pkvm_handle_t iommu_id, u32 endpoint_id, bool host_to_guest); int kvm_iommu_force_free_domain(pkvm_handle_t domain_id, struct pkvm_hyp_vm *vm); +int kvm_iommu_id_to_token(pkvm_handle_t smmu_id, u64 *out_token); struct kvm_iommu_ops { int (*init)(void); @@ -75,6 +76,7 @@ struct kvm_iommu_ops { int (*resume)(struct kvm_hyp_iommu *iommu); int (*dev_block_dma)(struct kvm_hyp_iommu *iommu, u32 endpoint_id, bool is_host_to_guest); + int (*get_iommu_token_by_id)(pkvm_handle_t smmu_id, u64 *out_token); ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h index 231abb8711bf..f6bb44264856 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h @@ -184,6 +184,7 @@ int pkvm_init_scmi_pd(struct kvm_power_domain *pd, const struct kvm_power_domain_ops *ops); bool pkvm_device_request_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code); +bool pkvm_device_request_dma(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code); void pkvm_devices_teardown(struct pkvm_hyp_vm *vm); int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id, struct pkvm_hyp_vm *vm); void pkvm_devices_put_context(u64 iommu_id, u32 endpoint_id); diff --git a/arch/arm64/kvm/hyp/nvhe/device/device.c b/arch/arm64/kvm/hyp/nvhe/device/device.c index 1bfa4022c96f..c4d0bb90b6b2 100644 --- a/arch/arm64/kvm/hyp/nvhe/device/device.c +++ b/arch/arm64/kvm/hyp/nvhe/device/device.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -419,3 +420,56 @@ int pkvm_device_register_reset(u64 phys, void *cookie, return ret; } + +bool pkvm_device_request_dma(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) +{ + int ret; + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); + struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; + u64 pviommu = smccc_get_arg1(vcpu); + u64 vsid = smccc_get_arg2(vcpu); + u64 token1, token2; + struct pviommu_route route; + struct pkvm_device *dev; + + if (smccc_get_arg3(vcpu) || smccc_get_arg4(vcpu) || smccc_get_arg5(vcpu) || + smccc_get_arg6(vcpu)) + goto out_ret; + + ret = pkvm_pviommu_get_route(vm, pviommu, vsid, &route); + if (ret) + goto out_ret; + token2 = route.sid; + /* + * route.iommu is the host-hyp iommu ID that has no meaning for guest. + * It needs to be converted to IOMMU token as in the firmware(usually + * base MMIO address). + */ + ret = kvm_iommu_id_to_token(route.iommu, &token1); + if (ret) + goto out_ret; + + dev = pkvm_get_device_by_iommu(route.iommu, route.sid); + if (!dev) + goto out_ret; + + hyp_spin_lock(&device_spinlock); + if (dev->ctxt == NULL) { + /* + * First time device is assigned to guest, make sure it's resources + * have been donated. + */ + ret = __pkvm_group_assign(dev->group_id, vm); + } else if (dev->ctxt != vm) { + ret = -EPERM; + } + hyp_spin_unlock(&device_spinlock); + if (ret) + goto out_ret; + + smccc_set_retval(vcpu, SMCCC_RET_SUCCESS, token1, token2, 0); + return true; +out_ret: + smccc_set_retval(vcpu, SMCCC_RET_INVALID_PARAMETER, 0, 0, 0); + return true; +} diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c index 06fc99b35a22..ce968846dee5 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c @@ -778,3 +778,10 @@ int kvm_iommu_snapshot_host_stage2(struct kvm_hyp_iommu_domain *domain) return ret; } + +int kvm_iommu_id_to_token(pkvm_handle_t id, u64 *out_token) +{ + if (!kvm_iommu_ops || !kvm_iommu_ops->get_iommu_token_by_id) + return -ENODEV; + return kvm_iommu_ops->get_iommu_token_by_id(id, out_token); +} diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index a71b1e49d605..532b1c154b1f 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -1759,6 +1759,8 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code) return kvm_handle_pviommu_hvc(vcpu, exit_code); case ARM_SMCCC_VENDOR_HYP_KVM_DEV_REQ_MMIO_FUNC_ID: return pkvm_device_request_mmio(hyp_vcpu, exit_code); + case ARM_SMCCC_VENDOR_HYP_KVM_DEV_REQ_DMA_FUNC_ID: + return pkvm_device_request_dma(hyp_vcpu, exit_code); default: if (is_ffa_call(fn)) return kvm_guest_ffa_handler(hyp_vcpu, exit_code); diff --git a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c index 767d47618196..6265e7af812f 100644 --- a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c @@ -1546,6 +1546,16 @@ static bool smmu_dabt_device(struct hyp_arm_smmu_v3_device *smmu, return true; } +static int smmu_id_to_token(pkvm_handle_t smmu_id, u64 *out_token) +{ + if (smmu_id >= kvm_hyp_arm_smmu_v3_count) + return -EINVAL; + + smmu_id = array_index_nospec(smmu_id, kvm_hyp_arm_smmu_v3_count); + *out_token = kvm_hyp_arm_smmu_v3_smmus[smmu_id].mmio_addr; + return 0; +} + static int smmu_dev_block_dma(struct kvm_hyp_iommu *iommu, u32 sid, bool is_host2guest) { struct hyp_arm_smmu_v3_device *smmu = to_smmu(iommu); @@ -1730,4 +1740,5 @@ struct kvm_iommu_ops smmu_ops = { .resume = smmu_resume, .host_stage2_idmap = smmu_host_stage2_idmap, .dev_block_dma = smmu_dev_block_dma, + .get_iommu_token_by_id = smmu_id_to_token, }; diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 31e00988404e..8769d75d8291 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -182,7 +182,7 @@ #define ARM_SMCCC_KVM_FUNC_PKVM_RESV_58 58 #define ARM_SMCCC_KVM_FUNC_PKVM_RESV_59 59 #define ARM_SMCCC_KVM_FUNC_PKVM_RESV_60 60 -#define ARM_SMCCC_KVM_FUNC_PKVM_RESV_61 61 +#define ARM_SMCCC_KVM_FUNC_DEV_REQ_DMA 61 #define ARM_SMCCC_KVM_FUNC_PVIOMMU_OP 62 #define ARM_SMCCC_KVM_FUNC_DEV_REQ_MMIO 63 /* End of pKVM hypercall range */ @@ -253,6 +253,12 @@ ARM_SMCCC_OWNER_VENDOR_HYP, \ ARM_SMCCC_KVM_FUNC_DEV_REQ_MMIO) +#define ARM_SMCCC_VENDOR_HYP_KVM_DEV_REQ_DMA_FUNC_ID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_VENDOR_HYP, \ + ARM_SMCCC_KVM_FUNC_DEV_REQ_DMA) + /* ptp_kvm counter type ID */ #define KVM_PTP_VIRT_COUNTER 0 #define KVM_PTP_PHYS_COUNTER 1