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 <dbrazdil@google.com>
Change-Id: Iec06697e8826b0dba682476b39cf64acd6337166
This commit is contained in:
David Brazdil
2022-02-14 22:40:09 +00:00
parent 667c7af580
commit 4c2ce4d16c
3 changed files with 42 additions and 50 deletions

View File

@@ -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__ */

View File

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

View File

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