diff --git a/arch/arm64/include/asm/kvm_s2mpu.h b/arch/arm64/include/asm/kvm_s2mpu.h index 6b9218673031..3d541accf982 100644 --- a/arch/arm64/include/asm/kvm_s2mpu.h +++ b/arch/arm64/include/asm/kvm_s2mpu.h @@ -24,6 +24,7 @@ #define REG_NS_INTERRUPT_ENABLE_PER_VID_SET 0x20 #define REG_NS_INTERRUPT_CLEAR 0x2c #define REG_NS_VERSION 0x60 +#define REG_NS_STATUS 0x68 #define REG_NS_NUM_CONTEXT 0x100 #define REG_NS_CONTEXT_CFG_VALID_VID 0x104 #define REG_NS_ALL_INVALIDATION 0x1000 @@ -67,6 +68,9 @@ VERSION_MINOR_ARCH_VER_MASK | \ VERSION_REV_ARCH_VER_MASK) +#define STATUS_BUSY BIT(0) +#define STATUS_ON_INVALIDATING BIT(1) + #define NUM_CONTEXT_MASK GENMASK(3, 0) #define CONTEXT_CFG_VALID_VID_CTX_VALID(ctx) BIT((4 * (ctx)) + 3) diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c index 89b2f0763780..6fa701ded195 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c @@ -113,10 +113,26 @@ static void __set_control_regs(struct s2mpu *dev) writel_relaxed(ctrl0, dev->va + REG_NS_CTRL0); } +/* Poll the given SFR as long as its value has all bits of a given mask set. */ +static void __wait_while(void __iomem *addr, u32 mask) +{ + while ((readl_relaxed(addr) & mask) == mask) + continue; +} + +static void __wait_for_invalidation_complete(struct s2mpu *dev) +{ + /* Must not access SFRs while S2MPU is busy invalidating (v9 only). */ + if (is_version(dev, S2MPU_VERSION_9)) { + __wait_while(dev->va + REG_NS_STATUS, + STATUS_BUSY | STATUS_ON_INVALIDATING); + } +} + static void __all_invalidation(struct s2mpu *dev) { - writel_relaxed(INVALIDATION_INVALIDATE, - dev->va + REG_NS_ALL_INVALIDATION); + writel_relaxed(INVALIDATION_INVALIDATE, dev->va + REG_NS_ALL_INVALIDATION); + __wait_for_invalidation_complete(dev); } static void __range_invalidation(struct s2mpu *dev, phys_addr_t first_byte, @@ -128,6 +144,7 @@ static void __range_invalidation(struct s2mpu *dev, phys_addr_t first_byte, writel_relaxed(start_ppn, dev->va + REG_NS_RANGE_INVALIDATION_START_PPN); writel_relaxed(end_ppn, dev->va + REG_NS_RANGE_INVALIDATION_END_PPN); writel_relaxed(INVALIDATION_INVALIDATE, dev->va + REG_NS_RANGE_INVALIDATION); + __wait_for_invalidation_complete(dev); } static void __set_l1entry_attr_with_prot(struct s2mpu *dev, unsigned int gb,