ANDROID: KVM: arm64: pviommu: Add alloc/free_domain() HVC ops

Guests doens't have a separate domain space as the host, but they share
the upper half of the domain ids, so they would ask for a domain and
get a domain_id as a return.

Also as the guests doesn't know about the IOMMU topology, add
KVM_IOMMU_DOMAIN_ANY_TYPE, which would be used for guests, and the
IOMMU driver should choose the proper type for it.

Allocating a domain for guest, this HVC can need memory for:
- PGD for the domain page table.
- Domain struct for hyp.

For both we return to host to fill the guest iommu memcache.

The HVC returns the ID allocated for this domain, which the guest
can use later for map/unmap.

Bug: 357781595
Bug: 348382247
Bug: 236685427
Change-Id: I2ee84938b60337bee38f2227db53c832878140d0
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
Mostafa Saleh
2023-04-16 10:19:20 +00:00
committed by Carlos Llamas
parent 540b4b9ce3
commit 38ab8e61f9
3 changed files with 98 additions and 4 deletions
+7
View File
@@ -303,6 +303,13 @@ int kvm_iommu_alloc_domain(pkvm_handle_t domain_id, int type)
struct pkvm_hyp_vcpu *hyp_vcpu = __get_vcpu();
struct pkvm_hyp_vm *vm;
/*
* Host only has access to the lower half of the domain IDs.
* Guest ID space is managed by the hypervisor, so it is trusted.
*/
if (!hyp_vcpu && (domain_id >= (KVM_IOMMU_MAX_DOMAINS >> 1)))
return -EINVAL;
domain = handle_to_domain(domain_id);
if (!domain)
return -ENOMEM;
+89 -4
View File
@@ -12,6 +12,40 @@
#include <nvhe/pviommu.h>
#include <nvhe/pviommu-host.h>
static DEFINE_HYP_SPINLOCK(pviommu_guest_domain_lock);
#define KVM_IOMMU_MAX_GUEST_DOMAINS (KVM_IOMMU_MAX_DOMAINS >> 1)
static unsigned long guest_domains[KVM_IOMMU_MAX_GUEST_DOMAINS / BITS_PER_LONG];
/*
* Guests doens't have separate domain space as the host, but they share the upper half
* of the domain ids, so they would ask for a domain and get a domain id as a return.
* This is a rare operation for guests, so bruteforcing the domain space should be fine
* for now, however we can improve this by having a hint for last allocated domain_id or
* use a pseudo-random number.
*/
static int pkvm_guest_iommu_alloc_id(void)
{
int i;
for (i = 0 ; i < ARRAY_SIZE(guest_domains) ; ++i) {
if (guest_domains[i] != ~0UL)
return ffz(guest_domains[i]) + i * BITS_PER_LONG +
(KVM_IOMMU_MAX_DOMAINS >> 1);
}
return -EBUSY;
}
static void pkvm_guest_iommu_free_id(int domain_id)
{
domain_id -= (KVM_IOMMU_MAX_DOMAINS >> 1);
if (WARN_ON(domain_id < 0) || (domain_id >= KVM_IOMMU_MAX_GUEST_DOMAINS))
return;
guest_domains[domain_id / BITS_PER_LONG] &= ~(1UL << (domain_id % BITS_PER_LONG));
}
static bool pkvm_guest_iommu_map(struct pkvm_hyp_vcpu *hyp_vcpu)
{
return false;
@@ -93,14 +127,65 @@ out_ret:
return true;
}
static bool pkvm_guest_iommu_alloc_domain(struct pkvm_hyp_vcpu *hyp_vcpu)
static bool pkvm_guest_iommu_alloc_domain(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code)
{
return false;
int ret;
int domain_id = 0;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
/* MBZ */
if (smccc_get_arg2(vcpu) || smccc_get_arg3(vcpu) || smccc_get_arg4(vcpu) ||
smccc_get_arg5(vcpu) || smccc_get_arg6(vcpu))
goto out_inval;
hyp_spin_lock(&pviommu_guest_domain_lock);
domain_id = pkvm_guest_iommu_alloc_id();
if (domain_id < 0)
goto out_inval;
ret = kvm_iommu_alloc_domain(domain_id, KVM_IOMMU_DOMAIN_ANY_TYPE);
if (ret == -ENOMEM) {
pkvm_guest_iommu_free_id(domain_id);
hyp_spin_unlock(&pviommu_guest_domain_lock);
pkvm_pviommu_hyp_req(exit_code);
return false;
} else if (ret) {
pkvm_guest_iommu_free_id(domain_id);
goto out_inval;
}
hyp_spin_unlock(&pviommu_guest_domain_lock);
smccc_set_retval(vcpu, SMCCC_RET_SUCCESS, domain_id, 0, 0);
return true;
out_inval:
hyp_spin_unlock(&pviommu_guest_domain_lock);
smccc_set_retval(vcpu, SMCCC_RET_INVALID_PARAMETER, 0, 0, 0);
return true;
}
static bool pkvm_guest_iommu_free_domain(struct pkvm_hyp_vcpu *hyp_vcpu)
{
return false;
int ret;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u64 domain_id = smccc_get_arg2(vcpu);
if (smccc_get_arg3(vcpu) || smccc_get_arg4(vcpu) || smccc_get_arg5(vcpu) ||
smccc_get_arg6(vcpu)) {
ret = -EINVAL;
goto out_ret;
}
hyp_spin_lock(&pviommu_guest_domain_lock);
ret = kvm_iommu_free_domain(domain_id);
if (!ret)
pkvm_guest_iommu_free_id(domain_id);
hyp_spin_unlock(&pviommu_guest_domain_lock);
out_ret:
smccc_set_retval(vcpu, ret ? SMCCC_RET_INVALID_PARAMETER : SMCCC_RET_SUCCESS,
0, 0, 0);
return true;
}
bool kvm_handle_pviommu_hvc(struct kvm_vcpu *vcpu, u64 *exit_code)
@@ -116,7 +201,7 @@ bool kvm_handle_pviommu_hvc(struct kvm_vcpu *vcpu, u64 *exit_code)
refill_hyp_pool(&vm->iommu_pool, &hyp_vcpu->host_vcpu->arch.iommu_mc);
switch (iommu_op) {
case KVM_PVIOMMU_OP_ALLOC_DOMAIN:
return pkvm_guest_iommu_alloc_domain(hyp_vcpu);
return pkvm_guest_iommu_alloc_domain(hyp_vcpu, exit_code);
case KVM_PVIOMMU_OP_FREE_DOMAIN:
return pkvm_guest_iommu_free_domain(hyp_vcpu);
case KVM_PVIOMMU_OP_ATTACH_DEV:
+2
View File
@@ -14,6 +14,8 @@
/* Used in alloc_domain type argument. */
#define KVM_IOMMU_DOMAIN_IDMAP_TYPE 0
/* Typically used for guests, as they don't know IOMMU topology. */
#define KVM_IOMMU_DOMAIN_ANY_TYPE 1
#define KVM_IOMMU_DOMAIN_NR_START (KVM_IOMMU_DOMAIN_IDMAP_ID + 1)