ANDROID: KVM: arm64: iommu: Erase pvmfw from EL1 if possible

Kernel IOMMU drivers can report system misconfiguration through
pkvm_iommu_finalize().
Then EL2 can take the proper response, for example if there are missing
IOMMUs, and DMA isolation can't be ensured, it would clear pvmfw so
PVMs can't be launched.
However, this is not clean as userspace can still query pvmfw info
and launch PVMs that loops in undefined instruction aborts as pvmfw
is cleared.
To fix this, before deprivilege, the kernel will erase pvmfw if the
IOMMUs are not finalised.

Bug: 268607700
Test: Launch PVM with missing S2MPU => Fail immediately with -8
Test: Launch PVM with all S2MPU => Pass
Change-Id: I9fd2440805f6b2f2ad4395ce61df5b272ed84fef
Signed-off-by: Mostafa Saleh <smostafa@google.com>
(cherry picked from commit 01ec18c52f65a3a784e236ce72aa983e65eca83c)
This commit is contained in:
Mostafa Saleh 2023-04-28 12:54:02 +00:00
parent 08f3189337
commit a0d46c1dd1
6 changed files with 23 additions and 25 deletions

View file

@ -406,6 +406,8 @@ int pkvm_iommu_resume(struct device *dev);
*/
int pkvm_iommu_finalize(int err);
bool pkvm_iommu_finalized(void);
struct vcpu_reset_state {
unsigned long pc;
unsigned long r0;

View file

@ -162,9 +162,4 @@ int pkvm_load_pvmfw_pages(struct pkvm_hyp_vm *vm, u64 ipa, phys_addr_t phys,
u64 size);
void pkvm_poison_pvmfw_pages(void);
/*
* Notify pKVM about events that can undermine pKVM security.
*/
void pkvm_handle_system_misconfiguration(enum pkvm_system_misconfiguration event);
#endif /* __ARM64_KVM_NVHE_PKVM_H__ */

View file

@ -456,6 +456,9 @@ int __pkvm_iommu_finalize(int err)
{
int ret = 0;
/* Err is not currently used in EL2.*/
WARN_ON(err);
hyp_spin_lock(&iommu_registration_lock);
if (!iommu_finalized)
iommu_finalized = true;
@ -463,13 +466,6 @@ int __pkvm_iommu_finalize(int err)
ret = -EPERM;
hyp_spin_unlock(&iommu_registration_lock);
/*
* If finalize failed in EL1 driver for any reason, this means we can't trust the DMA
* isolation. So we have to inform pKVM to properly protect itself.
*/
if (!ret && err)
pkvm_handle_system_misconfiguration(NO_DMA_ISOLATION);
return ret;
}

View file

@ -1570,14 +1570,3 @@ bool kvm_hyp_handle_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
return false;
}
/*
* Notify pKVM about events that can undermine pKVM security.
*/
void pkvm_handle_system_misconfiguration(enum pkvm_system_misconfiguration event)
{
if (event == NO_DMA_ISOLATION)
pkvm_poison_pvmfw_pages();
else
BUG();
}

View file

@ -6,6 +6,9 @@
#include <linux/kvm_host.h>
/* Did all IOMMUs register as expected. */
static bool finalised;
static unsigned long dev_to_id(struct device *dev)
{
/* Use the struct device pointer as a unique identifier. */
@ -59,6 +62,12 @@ EXPORT_SYMBOL(pkvm_iommu_resume);
int pkvm_iommu_finalize(int err)
{
return kvm_call_hyp_nvhe(__pkvm_iommu_finalize, err);
finalised = !err;
return kvm_call_hyp_nvhe(__pkvm_iommu_finalize, 0);
}
EXPORT_SYMBOL_GPL(pkvm_iommu_finalize);
bool pkvm_iommu_finalized(void)
{
return finalised;
}
EXPORT_SYMBOL(pkvm_iommu_finalize);

View file

@ -448,6 +448,9 @@ static int __init pkvm_firmware_rmem_clear(void)
return -EINVAL;
memset(addr, 0, size);
/* Clear so user space doesn't get stale info via IOCTL. */
pkvm_firmware_mem = NULL;
dcache_clean_poc((unsigned long)addr, (unsigned long)addr + size);
memunmap(addr);
return 0;
@ -501,6 +504,10 @@ static int __init finalize_pkvm(void)
if (pkvm_load_early_modules())
pkvm_firmware_rmem_clear();
/* If no DMA protection. */
if (!pkvm_iommu_finalized())
pkvm_firmware_rmem_clear();
/*
* Exclude HYP sections from kmemleak so that they don't get peeked
* at, which would end badly once inaccessible.