ANDROID: KVM: arm64: Wait on S2MPU.STATUS after invalidation

The S2MPU must wait for a v9 device to finish invalidation before
accessing its SFRs. Failure to do so can result in memory transaction
timeouts.

Add a loop that polls the STATUS register while the return value has
the BUSY and ON_INVALIDATING bits set.

Test: builds, boots
Bug: 190463801
Bug: 206761586
Signed-off-by: David Brazdil <dbrazdil@google.com>
Change-Id: I00891dc3a8ad185d29757b8622a053a96237b803
This commit is contained in:
David Brazdil
2022-01-10 11:17:54 +00:00
parent 4377d9dea9
commit e149939df2
2 changed files with 23 additions and 2 deletions

View File

@@ -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)

View File

@@ -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,