ANDROID: KVM: arm64: pviommu: Add attach/detach_dev() guest HVC ops

Add attach/detach_dev() guest HVC ops implementation.

If -ENOMEM returned, the HVC is retried but first it returns to host to
refill the vcpu IOMMU mem cache.

Bug: 357781595
Bug: 348382247
Bug: 236685427
Change-Id: I11338878857f6c4be6c756b23139e1ba8584dbdf
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
Mostafa Saleh
2023-04-07 21:05:51 +00:00
committed by Carlos Llamas
parent 6b871d2a3c
commit 8a9430e18b
4 changed files with 81 additions and 13 deletions
+1 -1
View File
@@ -185,7 +185,7 @@ int pkvm_init_scmi_pd(struct kvm_power_domain *pd,
bool pkvm_device_request_mmio(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);
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);
/*
+2 -4
View File
@@ -382,7 +382,7 @@ static struct pkvm_device *pkvm_get_device_by_iommu(u64 id, u32 endpoint_id)
return NULL;
}
int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id)
int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id, struct pkvm_hyp_vm *vm)
{
struct pkvm_device *dev = pkvm_get_device_by_iommu(iommu_id, endpoint_id);
int ret = 0;
@@ -391,7 +391,7 @@ int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id)
return 0;
hyp_spin_lock(&device_spinlock);
if (dev->ctxt)
if (dev->ctxt != vm)
ret = -EPERM;
else
hyp_refcount_inc(dev->refcount);
@@ -407,9 +407,7 @@ void pkvm_devices_put_context(u64 iommu_id, u32 endpoint_id)
return;
hyp_spin_lock(&device_spinlock);
BUG_ON(dev->ctxt);
hyp_refcount_dec(dev->refcount);
hyp_spin_unlock(&device_spinlock);
}
+11 -4
View File
@@ -336,16 +336,19 @@ int kvm_iommu_attach_dev(pkvm_handle_t iommu_id, pkvm_handle_t domain_id,
int ret;
struct kvm_hyp_iommu *iommu;
struct kvm_hyp_iommu_domain *domain;
struct pkvm_hyp_vcpu *hyp_vcpu = __get_vcpu();
struct pkvm_hyp_vm *vm = NULL;
iommu = kvm_iommu_ops->get_iommu_by_id(iommu_id);
if (!iommu)
return -EINVAL;
if (hyp_vcpu)
vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
/*
* At the moment the IOMMU in EL2 is not aware of guests and pvIOMMU
* doesn't exist yet, so all attaches come from host, this should change soon.
* Make sure device can't transition to/from VMs while in the middle of attach.
*/
ret = pkvm_devices_get_context(iommu_id, endpoint_id);
ret = pkvm_devices_get_context(iommu_id, endpoint_id, vm);
if (ret)
return ret;
@@ -370,13 +373,17 @@ int kvm_iommu_detach_dev(pkvm_handle_t iommu_id, pkvm_handle_t domain_id,
int ret;
struct kvm_hyp_iommu *iommu;
struct kvm_hyp_iommu_domain *domain;
struct pkvm_hyp_vcpu *hyp_vcpu = __get_vcpu();
struct pkvm_hyp_vm *vm = NULL;
iommu = kvm_iommu_ops->get_iommu_by_id(iommu_id);
if (!iommu)
return -EINVAL;
if (hyp_vcpu)
vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
/* See kvm_iommu_attach_dev(). */
ret = pkvm_devices_get_context(iommu_id, endpoint_id);
ret = pkvm_devices_get_context(iommu_id, endpoint_id, vm);
if (ret)
return ret;
+67 -4
View File
@@ -6,9 +6,11 @@
#include <kvm/arm_hypercalls.h>
#include <nvhe/iommu.h>
#include <nvhe/mem_protect.h>
#include <nvhe/pkvm.h>
#include <nvhe/pviommu.h>
#include <nvhe/pviommu-host.h>
static bool pkvm_guest_iommu_map(struct pkvm_hyp_vcpu *hyp_vcpu)
{
@@ -20,14 +22,75 @@ static bool pkvm_guest_iommu_unmap(struct pkvm_hyp_vcpu *hyp_vcpu)
return false;
}
static bool pkvm_guest_iommu_attach_dev(struct pkvm_hyp_vcpu *hyp_vcpu)
static void pkvm_pviommu_hyp_req(u64 *exit_code)
{
return false;
write_sysreg_el2(read_sysreg_el2(SYS_ELR) - 4, SYS_ELR);
*exit_code = ARM_EXCEPTION_HYP_REQ;
}
static bool pkvm_guest_iommu_attach_dev(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code)
{
int ret;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u64 iommu_id = smccc_get_arg2(vcpu);
u64 sid = smccc_get_arg3(vcpu);
u64 pasid = smccc_get_arg4(vcpu);
u64 domain_id = smccc_get_arg5(vcpu);
u64 pasid_bits = smccc_get_arg6(vcpu);
struct pviommu_route route;
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
ret = pkvm_pviommu_get_route(vm, iommu_id, sid, &route);
if (ret)
goto out_ret;
iommu_id = route.iommu;
sid = route.sid;
ret = kvm_iommu_attach_dev(iommu_id, domain_id, sid, pasid, pasid_bits);
if (ret == -ENOMEM) {
/*
* The driver will request memory when returning -ENOMEM, so go back to host to
* fulfill the request and repeat the HVC.
*/
pkvm_pviommu_hyp_req(exit_code);
return false;
}
out_ret:
smccc_set_retval(vcpu, ret ? SMCCC_RET_INVALID_PARAMETER : SMCCC_RET_SUCCESS,
0, 0, 0);
return true;
}
static bool pkvm_guest_iommu_detach_dev(struct pkvm_hyp_vcpu *hyp_vcpu)
{
return false;
int ret;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u64 iommu_id = smccc_get_arg2(vcpu);
u64 sid = smccc_get_arg3(vcpu);
u64 pasid = smccc_get_arg4(vcpu);
u64 domain_id = smccc_get_arg5(vcpu);
struct pviommu_route route;
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
/* MBZ */
if (smccc_get_arg6(vcpu)) {
ret = -EINVAL;
goto out_ret;
}
ret = pkvm_pviommu_get_route(vm, iommu_id, sid, &route);
if (ret)
goto out_ret;
iommu_id = route.iommu;
sid = route.sid;
ret = kvm_iommu_detach_dev(iommu_id, domain_id, sid, pasid);
out_ret:
smccc_set_retval(vcpu, ret ? SMCCC_RET_INVALID_PARAMETER : SMCCC_RET_SUCCESS,
0, 0, 0);
return true;
}
static bool pkvm_guest_iommu_alloc_domain(struct pkvm_hyp_vcpu *hyp_vcpu)
@@ -57,7 +120,7 @@ bool kvm_handle_pviommu_hvc(struct kvm_vcpu *vcpu, u64 *exit_code)
case KVM_PVIOMMU_OP_FREE_DOMAIN:
return pkvm_guest_iommu_free_domain(hyp_vcpu);
case KVM_PVIOMMU_OP_ATTACH_DEV:
return pkvm_guest_iommu_attach_dev(hyp_vcpu);
return pkvm_guest_iommu_attach_dev(hyp_vcpu, exit_code);
case KVM_PVIOMMU_OP_DETACH_DEV:
return pkvm_guest_iommu_detach_dev(hyp_vcpu);
case KVM_PVIOMMU_OP_MAP_PAGES: