From 4c2ce4d16cc63f3d03da8411b876e9cc1fa3c52f Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 14 Feb 2022 22:40:09 +0000 Subject: [PATCH] ANDROID: KVM: arm64: s2mpu: Move mpt_update_flags into FMPT Core SMPT manipulation code returns mpt_update_flags, signalling whether the caller should flush the dcache (MPT_UPDATE_L2) or write new L1ATTR values to S2MPU MMIO registers (MPT_UPDATE_L1). In preparation for splitting the code into a driver-global and per-device portions, store the value in the corresponding FMPT. As long as the two code portions are called from a single critical section, the FMPT value is guaranteed to not change. Bug: 190463801 Signed-off-by: David Brazdil Change-Id: Iec06697e8826b0dba682476b39cf64acd6337166 --- arch/arm64/include/asm/kvm_s2mpu.h | 42 +++++++++++++++----------- arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c | 7 ++--- arch/arm64/kvm/iommu/test_kvm_s2mpu.c | 43 +++++++++------------------ 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/arch/arm64/include/asm/kvm_s2mpu.h b/arch/arm64/include/asm/kvm_s2mpu.h index 0e322ede8421..4d517bc1d0eb 100644 --- a/arch/arm64/include/asm/kvm_s2mpu.h +++ b/arch/arm64/include/asm/kvm_s2mpu.h @@ -160,21 +160,22 @@ static const u64 mpt_prot_doubleword[] = { [MPT_PROT_RW] = 0xffffffffffffffff, }; +enum mpt_update_flags { + MPT_UPDATE_L1 = BIT(0), + MPT_UPDATE_L2 = BIT(1), +}; + struct fmpt { u32 *smpt; bool gran_1g; enum mpt_prot prot; + enum mpt_update_flags flags; }; struct mpt { struct fmpt fmpt[NR_GIGABYTES]; }; -enum mpt_update_flags { - MPT_UPDATE_L1 = BIT(0), - MPT_UPDATE_L2 = BIT(1), -}; - /* Set protection bits of SMPT in a given range without using memset. */ static inline void __set_smpt_range_slow(u32 *smpt, size_t start_gb_byte, size_t end_gb_byte, enum mpt_prot prot) @@ -254,24 +255,28 @@ static inline bool __is_smpt_uniform(u32 *smpt, enum mpt_prot prot) * Returns flags specifying whether L1/L2 changes need to be made visible * to the device. */ -static inline enum mpt_update_flags -__set_fmpt_range(struct fmpt *fmpt, size_t start_gb_byte, size_t end_gb_byte, - enum mpt_prot prot) +static inline void __set_fmpt_range(struct fmpt *fmpt, size_t start_gb_byte, + size_t end_gb_byte, enum mpt_prot prot) { if (start_gb_byte == 0 && end_gb_byte >= SZ_1G) { /* Update covers the entire GB region. */ - if (fmpt->gran_1g && fmpt->prot == prot) - return 0; + if (fmpt->gran_1g && fmpt->prot == prot) { + fmpt->flags = 0; + return; + } fmpt->gran_1g = true; fmpt->prot = prot; - return MPT_UPDATE_L1; + fmpt->flags = MPT_UPDATE_L1; + return; } if (fmpt->gran_1g) { /* GB region currently uses 1G mapping. */ - if (fmpt->prot == prot) - return 0; + if (fmpt->prot == prot) { + fmpt->flags = 0; + return; + } /* * Range has different mapping than the rest of the GB. @@ -281,19 +286,22 @@ __set_fmpt_range(struct fmpt *fmpt, size_t start_gb_byte, size_t end_gb_byte, __set_smpt_range(fmpt->smpt, 0, start_gb_byte, fmpt->prot); __set_smpt_range(fmpt->smpt, start_gb_byte, end_gb_byte, prot); __set_smpt_range(fmpt->smpt, end_gb_byte, SZ_1G, fmpt->prot); - return MPT_UPDATE_L1 | MPT_UPDATE_L2; + fmpt->flags = MPT_UPDATE_L1 | MPT_UPDATE_L2; + return; } /* GB region currently uses PAGE_SIZE mapping. */ __set_smpt_range(fmpt->smpt, start_gb_byte, end_gb_byte, prot); /* Check if the entire GB region has the same prot bits. */ - if (!__is_smpt_uniform(fmpt->smpt, prot)) - return MPT_UPDATE_L2; + if (!__is_smpt_uniform(fmpt->smpt, prot)) { + fmpt->flags = MPT_UPDATE_L2; + return; + } fmpt->gran_1g = true; fmpt->prot = prot; - return MPT_UPDATE_L1; + fmpt->flags = MPT_UPDATE_L1; } #endif /* __ARM64_KVM_S2MPU_H__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c index cd621190120f..a1001f8f9aaf 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c @@ -285,19 +285,18 @@ static void set_mpt_range_locked(struct mpt *mpt, phys_addr_t first_byte, unsigned int gb, vid; struct pkvm_iommu *dev; struct fmpt *fmpt; - enum mpt_update_flags flags; for_each_gb_in_range(gb, first_gb, last_gb) { fmpt = &mpt->fmpt[gb]; start_gb_byte = (gb == first_gb) ? first_byte % SZ_1G : 0; end_gb_byte = (gb == last_gb) ? (last_byte % SZ_1G) + 1 : SZ_1G; - flags = __set_fmpt_range(fmpt, start_gb_byte, end_gb_byte, prot); + __set_fmpt_range(fmpt, start_gb_byte, end_gb_byte, prot); - if (flags & MPT_UPDATE_L2) + if (fmpt->flags & MPT_UPDATE_L2) kvm_flush_dcache_to_poc(fmpt->smpt, SMPT_SIZE); - if (flags & MPT_UPDATE_L1) { + if (fmpt->flags & MPT_UPDATE_L1) { for_each_powered_s2mpu(dev) { for_each_vid(vid) __set_l1entry_attr_with_fmpt(dev, gb, vid, fmpt); diff --git a/arch/arm64/kvm/iommu/test_kvm_s2mpu.c b/arch/arm64/kvm/iommu/test_kvm_s2mpu.c index 67cc37f00dee..fbc500de588e 100644 --- a/arch/arm64/kvm/iommu/test_kvm_s2mpu.c +++ b/arch/arm64/kvm/iommu/test_kvm_s2mpu.c @@ -14,10 +14,6 @@ KSTM_MODULE_GLOBALS(); -/* - * Kernel module for testing the foobinator - */ - #define ASSERT(cond) \ do { \ if (!(cond)) { \ @@ -77,11 +73,9 @@ static bool __init check_smpt(size_t start_byte, size_t end_byte, /* Start with 1G granule, overwrite the whole 1G. */ static int __init test_set_fmpt__fmpt_to_fmpt_whole(void) { - enum mpt_update_flags flags; - init_fmpt(MPT_PROT_NONE, /*gran_1g*/ true); - flags = __set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R); - ASSERT(flags == MPT_UPDATE_L1); + __set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R); + ASSERT(g_fmpt.flags == MPT_UPDATE_L1); ASSERT(g_fmpt.gran_1g); ASSERT(g_fmpt.prot == MPT_PROT_R); return 0; @@ -90,11 +84,9 @@ static int __init test_set_fmpt__fmpt_to_fmpt_whole(void) /* Start with 1G granule, overwrite the whole 1G with the same prot. */ static int __init test_set_fmpt__fmpt_no_change_whole(void) { - enum mpt_update_flags flags; - init_fmpt(MPT_PROT_R, /*gran_1g*/ true); - flags = __set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R); - ASSERT(flags == 0); + __set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R); + ASSERT(g_fmpt.flags == 0); ASSERT(g_fmpt.gran_1g); ASSERT(g_fmpt.prot == MPT_PROT_R); return 0; @@ -103,11 +95,9 @@ static int __init test_set_fmpt__fmpt_no_change_whole(void) /* Start with 1G granule, partially overwrite with the same prot. */ static int __init test_set_fmpt__fmpt_no_change_partial(void) { - enum mpt_update_flags flags; - init_fmpt(MPT_PROT_R, /*gran_1g*/ true); - flags = __set_fmpt_range(&g_fmpt, 0, PAGE_SIZE, MPT_PROT_R); - ASSERT(flags == 0); + __set_fmpt_range(&g_fmpt, 0, PAGE_SIZE, MPT_PROT_R); + ASSERT(g_fmpt.flags == 0); ASSERT(g_fmpt.gran_1g); ASSERT(g_fmpt.prot == MPT_PROT_R); return 0; @@ -118,11 +108,10 @@ static int __init test_set_fmpt__fmpt_to_smpt(void) { size_t start = 5 * SMPT_WORD_BYTE_RANGE / 2; size_t end = 20 * SMPT_WORD_BYTE_RANGE; - enum mpt_update_flags flags; init_fmpt(MPT_PROT_R, /*gran_1g*/ true); - flags = __set_fmpt_range(&g_fmpt, start, end, MPT_PROT_RW); - ASSERT(flags == (MPT_UPDATE_L1 | MPT_UPDATE_L2)); + __set_fmpt_range(&g_fmpt, start, end, MPT_PROT_RW); + ASSERT(g_fmpt.flags == (MPT_UPDATE_L1 | MPT_UPDATE_L2)); ASSERT(!g_fmpt.gran_1g); return check_smpt(start, end, MPT_PROT_R, MPT_PROT_RW) ? 0 : 1; } @@ -130,11 +119,9 @@ static int __init test_set_fmpt__fmpt_to_smpt(void) /* Convert from PAGE_SIZE to 1G granule by overwriting the whole 1G. */ static int __init test_set_fmpt__smpt_to_fmpt_whole(void) { - enum mpt_update_flags flags; - init_fmpt(MPT_PROT_NONE, /*gran_1g*/ false); - flags = __set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R); - ASSERT(flags == MPT_UPDATE_L1); + __set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R); + ASSERT(g_fmpt.flags == MPT_UPDATE_L1); ASSERT(g_fmpt.gran_1g); ASSERT(g_fmpt.prot == MPT_PROT_R); return 0; @@ -145,15 +132,14 @@ static int __init test_set_fmpt__smpt_to_fmpt_partial(void) { size_t start = 5 * SMPT_WORD_BYTE_RANGE / 2; size_t end = 20 * SMPT_WORD_BYTE_RANGE; - enum mpt_update_flags flags; /* Create SMPT with all PROT_W except a small subrange. */ init_fmpt(MPT_PROT_W, /*gran_1g*/ false); __set_smpt_range(g_smpt, start, end, MPT_PROT_RW); /* Fill the subrange with PROT_W to make the SMPT uniform. */ - flags = __set_fmpt_range(&g_fmpt, start, end, MPT_PROT_W); - ASSERT(flags == MPT_UPDATE_L1); + __set_fmpt_range(&g_fmpt, start, end, MPT_PROT_W); + ASSERT(g_fmpt.flags == MPT_UPDATE_L1); ASSERT(g_fmpt.gran_1g); ASSERT(g_fmpt.prot == MPT_PROT_W); return 0; @@ -164,14 +150,13 @@ static int __init test_set_fmpt__smpt_to_smpt(void) { size_t start = SZ_1G - SMPT_GRAN; size_t end = SZ_1G; - enum mpt_update_flags flags; init_fmpt(MPT_PROT_NONE, /*gran_1g*/ false); ASSERT(__is_smpt_uniform(g_smpt, MPT_PROT_NONE)); /* Fill the subrange with PROT_W to make the SMPT uniform. */ - flags = __set_fmpt_range(&g_fmpt, start, end, MPT_PROT_RW); - ASSERT(flags == MPT_UPDATE_L2); + __set_fmpt_range(&g_fmpt, start, end, MPT_PROT_RW); + ASSERT(g_fmpt.flags == MPT_UPDATE_L2); ASSERT(!g_fmpt.gran_1g); ASSERT(!__is_smpt_uniform(g_smpt, MPT_PROT_NONE)); return 0;