mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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:
@@ -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__ */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user