[PATCH v4 25/28] iommu/arm-smmu-v3-kvm: Emulate GBPA

Mostafa Saleh
2025-08-19T21:52:49Z

The last bit of emulation is GBPA. it must be always set to ABORT,
as when the SMMU is disabled it’s not allowed for the host to bypass
the SMMU.

That ‘s is done by setting the GBPA to ABORT at init time, when the host:
- Writes, we ignore the write and save the value without the UPDATE bit.
- Reads, return the saved value.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
.../iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c | 21 ++++++++++++++++---
.../iommu/arm/arm-smmu-v3/pkvm/arm_smmu_v3.h | 2 ++
2 files changed, 20 insertions(+), 3 deletions(-)

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 0f890a7d8db3..db9d9caaca2c 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
@@ -100,6 +100,13 @@ static int smmu_unshare_pages(phys_addr_t addr, size_t size)
return 0;
}

+static int smmu_abort_gbpa(struct hyp_arm_smmu_v3_device *smmu)
+{
+ writel_relaxed(GBPA_UPDATE | GBPA_ABORT, smmu->base + ARM_SMMU_GBPA);
+ /* Wait till UPDATE is cleared. */
+ return smmu_wait(readl_relaxed(smmu->base + ARM_SMMU_GBPA) == GBPA_ABORT);
+}
+
static bool smmu_cmdq_full(struct arm_smmu_queue *cmdq)
{
struct arm_smmu_ll_queue *llq = &cmdq->llq;
@@ -416,6 +423,10 @@ static int smmu_init_device(struct hyp_arm_smmu_v3_device *smmu)
if (ret)
goto out_ret;

+ ret = smmu_abort_gbpa(smmu);
+ if (ret)
+ goto out_ret;
+
return 0;

out_ret:
@@ -663,10 +674,14 @@ static bool smmu_dabt_device(struct hyp_arm_smmu_v3_device *smmu,
smmu->host_ste_cfg = val;
}
goto out_ret;
- /* Passthrough the register access for bisectiblity, handled later */
case ARM_SMMU_GBPA:
- mask = read_write;
- break;
+ if (is_write)
+ smmu->gbpa = val & ~GBPA_UPDATE;
+ else
+ regs->regs[rd] = smmu->gbpa;
+
+ WARN_ON(len != sizeof(u32));
+ goto out_ret;
case ARM_SMMU_CR0:
if (is_write) {
bool last_cmdq_en = is_cmdq_enabled(smmu);
diff --git a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm_smmu_v3.h b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm_smmu_v3.h
index cf85e5efdd9e..aab585dd9fd8 100644
--- a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm_smmu_v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm_smmu_v3.h
@@ -31,6 +31,7 @@
* @host_ste_cfg Host stream table config
* @host_ste_base Host stream table base
* @strtab_cfg Stream table as seen by HW
+ * @gbpa Last value of GBPA from the host
*/
struct hyp_arm_smmu_v3_device {
phys_addr_t mmio_addr;
@@ -54,6 +55,7 @@ struct hyp_arm_smmu_v3_device {
u64 host_ste_cfg;
u64 host_ste_base;
struct arm_smmu_strtab_cfg strtab_cfg;
+ u32 gbpa;
};

extern size_t kvm_nvhe_sym(kvm_hyp_arm_smmu_v3_count);
--
2.51.0.rc1.167.g924127e9c0-goog