BACKPORT: FROMLIST: KVM: arm64: smmu-v3: Initialize registers

Ensure all writable registers are properly initialized. We do not touch
registers that will not be read by the SMMU due to disabled features.

Link: https://lore.kernel.org/all/20241212180423.1578358-27-smostafa@google.com/

Bug: 357781595
Bug: 384432312

Change-Id: I2f8ed283765c3b9fe577239be96b84164a6a23f1
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
Jean-Philippe Brucker
2024-12-12 18:03:50 +00:00
committed by Mostafa Saleh
parent c16a84d8e6
commit b414161ca3
2 changed files with 140 additions and 1 deletions
+129 -1
View File
@@ -4,16 +4,144 @@
*
* Copyright (C) 2022 Linaro Ltd.
*/
#include <asm/arm-smmu-v3-common.h>
#include <asm/kvm_hyp.h>
#include <kvm/arm_smmu_v3.h>
#include <nvhe/iommu.h>
#include <nvhe/mem_protect.h>
#include <nvhe/mm.h>
#include <nvhe/pkvm.h>
#define ARM_SMMU_POLL_TIMEOUT_US 100000 /* 100ms arbitrary timeout */
size_t __ro_after_init kvm_hyp_arm_smmu_v3_count;
struct hyp_arm_smmu_v3_device *kvm_hyp_arm_smmu_v3_smmus;
#define for_each_smmu(smmu) \
for ((smmu) = kvm_hyp_arm_smmu_v3_smmus; \
(smmu) != &kvm_hyp_arm_smmu_v3_smmus[kvm_hyp_arm_smmu_v3_count]; \
(smmu)++)
/*
* Wait until @cond is true.
* Return 0 on success, or -ETIMEDOUT
*/
#define smmu_wait(_cond) \
({ \
int __i = 0; \
int __ret = 0; \
\
while (!(_cond)) { \
if (++__i > ARM_SMMU_POLL_TIMEOUT_US) { \
__ret = -ETIMEDOUT; \
break; \
} \
pkvm_udelay(1); \
} \
__ret; \
})
static int smmu_write_cr0(struct hyp_arm_smmu_v3_device *smmu, u32 val)
{
writel_relaxed(val, smmu->base + ARM_SMMU_CR0);
return smmu_wait(readl_relaxed(smmu->base + ARM_SMMU_CR0ACK) == val);
}
/* Transfer ownership of structures from host to hyp */
static int smmu_take_pages(u64 phys, size_t size)
{
WARN_ON(!PAGE_ALIGNED(phys) || !PAGE_ALIGNED(size));
return __pkvm_host_donate_hyp(phys >> PAGE_SHIFT, size >> PAGE_SHIFT);
}
static void smmu_reclaim_pages(u64 phys, size_t size)
{
WARN_ON(!PAGE_ALIGNED(phys) || !PAGE_ALIGNED(size));
WARN_ON(__pkvm_hyp_donate_host(phys >> PAGE_SHIFT, size >> PAGE_SHIFT));
}
static int smmu_init_registers(struct hyp_arm_smmu_v3_device *smmu)
{
u64 val, old;
int ret;
if (!(readl_relaxed(smmu->base + ARM_SMMU_GBPA) & GBPA_ABORT))
return -EINVAL;
/* Initialize all RW registers that will be read by the SMMU */
ret = smmu_write_cr0(smmu, 0);
if (ret)
return ret;
val = FIELD_PREP(CR1_TABLE_SH, ARM_SMMU_SH_ISH) |
FIELD_PREP(CR1_TABLE_OC, CR1_CACHE_WB) |
FIELD_PREP(CR1_TABLE_IC, CR1_CACHE_WB) |
FIELD_PREP(CR1_QUEUE_SH, ARM_SMMU_SH_ISH) |
FIELD_PREP(CR1_QUEUE_OC, CR1_CACHE_WB) |
FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB);
writel_relaxed(val, smmu->base + ARM_SMMU_CR1);
writel_relaxed(CR2_PTM, smmu->base + ARM_SMMU_CR2);
writel_relaxed(0, smmu->base + ARM_SMMU_IRQ_CTRL);
val = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
old = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
/* Service Failure Mode is fatal */
if ((val ^ old) & GERROR_SFM_ERR)
return -EIO;
/* Clear pending errors */
writel_relaxed(val, smmu->base + ARM_SMMU_GERRORN);
return 0;
}
static int smmu_init_device(struct hyp_arm_smmu_v3_device *smmu)
{
int ret;
if (!PAGE_ALIGNED(smmu->mmio_addr | smmu->mmio_size))
return -EINVAL;
ret = ___pkvm_host_donate_hyp(smmu->mmio_addr >> PAGE_SHIFT,
smmu->mmio_size >> PAGE_SHIFT,
/* accept_mmio */ true);
if (ret)
return ret;
smmu->base = hyp_phys_to_virt(smmu->mmio_addr);
ret = smmu_init_registers(smmu);
if (ret)
return ret;
return kvm_iommu_init_device(&smmu->iommu);
}
static int smmu_init(void)
{
return -ENOSYS;
int ret;
struct hyp_arm_smmu_v3_device *smmu;
size_t smmu_arr_size = PAGE_ALIGN(sizeof(*kvm_hyp_arm_smmu_v3_smmus) *
kvm_hyp_arm_smmu_v3_count);
phys_addr_t smmu_arr_phys;
kvm_hyp_arm_smmu_v3_smmus = kern_hyp_va(kvm_hyp_arm_smmu_v3_smmus);
smmu_arr_phys = hyp_virt_to_phys(kvm_hyp_arm_smmu_v3_smmus);
ret = smmu_take_pages(smmu_arr_phys, smmu_arr_size);
if (ret)
return ret;
for_each_smmu(smmu) {
ret = smmu_init_device(smmu);
if (ret)
goto out_reclaim_smmu;
}
return 0;
out_reclaim_smmu:
smmu_reclaim_pages(smmu_arr_phys, smmu_arr_size);
return ret;
}
/* Shared with the kernel driver in EL1 */
+11
View File
@@ -5,8 +5,19 @@
#include <asm/kvm_asm.h>
#include <kvm/iommu.h>
/*
* Parameters from the trusted host:
* @mmio_addr base address of the SMMU registers
* @mmio_size size of the registers resource
*
* Other members are filled and used at runtime by the SMMU driver.
*/
struct hyp_arm_smmu_v3_device {
struct kvm_hyp_iommu iommu;
phys_addr_t mmio_addr;
size_t mmio_size;
void __iomem *base;
};
extern size_t kvm_nvhe_sym(kvm_hyp_arm_smmu_v3_count);