Skip to content

Commit d0e22b4

Browse files
Marc Zyngierchazy
authored andcommitted
KVM: arm/arm64: Limit icache invalidation to prefetch aborts
We've so far eagerly invalidated the icache, no matter how the page was faulted in (data or prefetch abort). But we can easily track execution by setting the XN bits in the S2 page tables, get the prefetch abort at HYP and perform the icache invalidation at that time only. As for most VMs, the instruction working set is pretty small compared to the data set, this is likely to save some traffic (specially as the invalidation is broadcast). Reviewed-by: Christoffer Dall <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Signed-off-by: Christoffer Dall <[email protected]>
1 parent fefb876 commit d0e22b4

File tree

5 files changed

+43
-8
lines changed

5 files changed

+43
-8
lines changed

arch/arm/include/asm/kvm_mmu.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ static inline pmd_t kvm_s2pmd_mkwrite(pmd_t pmd)
8585
return pmd;
8686
}
8787

88+
static inline pte_t kvm_s2pte_mkexec(pte_t pte)
89+
{
90+
pte_val(pte) &= ~L_PTE_XN;
91+
return pte;
92+
}
93+
94+
static inline pmd_t kvm_s2pmd_mkexec(pmd_t pmd)
95+
{
96+
pmd_val(pmd) &= ~PMD_SECT_XN;
97+
return pmd;
98+
}
99+
88100
static inline void kvm_set_s2pte_readonly(pte_t *pte)
89101
{
90102
pte_val(*pte) = (pte_val(*pte) & ~L_PTE_S2_RDWR) | L_PTE_S2_RDONLY;

arch/arm/include/asm/pgtable.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ extern pgprot_t pgprot_s2_device;
102102
#define PAGE_HYP_EXEC _MOD_PROT(pgprot_kernel, L_PTE_HYP | L_PTE_RDONLY)
103103
#define PAGE_HYP_RO _MOD_PROT(pgprot_kernel, L_PTE_HYP | L_PTE_RDONLY | L_PTE_XN)
104104
#define PAGE_HYP_DEVICE _MOD_PROT(pgprot_hyp_device, L_PTE_HYP)
105-
#define PAGE_S2 _MOD_PROT(pgprot_s2, L_PTE_S2_RDONLY)
106-
#define PAGE_S2_DEVICE _MOD_PROT(pgprot_s2_device, L_PTE_S2_RDONLY)
105+
#define PAGE_S2 _MOD_PROT(pgprot_s2, L_PTE_S2_RDONLY | L_PTE_XN)
106+
#define PAGE_S2_DEVICE _MOD_PROT(pgprot_s2_device, L_PTE_S2_RDONLY | L_PTE_XN)
107107

108108
#define __PAGE_NONE __pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN | L_PTE_NONE)
109109
#define __PAGE_SHARED __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN)

arch/arm64/include/asm/kvm_mmu.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ static inline pmd_t kvm_s2pmd_mkwrite(pmd_t pmd)
173173
return pmd;
174174
}
175175

176+
static inline pte_t kvm_s2pte_mkexec(pte_t pte)
177+
{
178+
pte_val(pte) &= ~PTE_S2_XN;
179+
return pte;
180+
}
181+
182+
static inline pmd_t kvm_s2pmd_mkexec(pmd_t pmd)
183+
{
184+
pmd_val(pmd) &= ~PMD_S2_XN;
185+
return pmd;
186+
}
187+
176188
static inline void kvm_set_s2pte_readonly(pte_t *pte)
177189
{
178190
pteval_t old_pteval, pteval;

arch/arm64/include/asm/pgtable-prot.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@
6060
#define PAGE_HYP_RO __pgprot(_PAGE_DEFAULT | PTE_HYP | PTE_RDONLY | PTE_HYP_XN)
6161
#define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP)
6262

63-
#define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY)
64-
#define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN)
63+
#define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY | PTE_S2_XN)
64+
#define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_S2_XN)
6565

6666
#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_PXN | PTE_UXN)
6767
#define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)

virt/kvm/arm/mmu.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,7 +1292,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
12921292
unsigned long fault_status)
12931293
{
12941294
int ret;
1295-
bool write_fault, writable, hugetlb = false, force_pte = false;
1295+
bool write_fault, exec_fault, writable, hugetlb = false, force_pte = false;
12961296
unsigned long mmu_seq;
12971297
gfn_t gfn = fault_ipa >> PAGE_SHIFT;
12981298
struct kvm *kvm = vcpu->kvm;
@@ -1304,7 +1304,10 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
13041304
unsigned long flags = 0;
13051305

13061306
write_fault = kvm_is_write_fault(vcpu);
1307-
if (fault_status == FSC_PERM && !write_fault) {
1307+
exec_fault = kvm_vcpu_trap_is_iabt(vcpu);
1308+
VM_BUG_ON(write_fault && exec_fault);
1309+
1310+
if (fault_status == FSC_PERM && !write_fault && !exec_fault) {
13081311
kvm_err("Unexpected L2 read permission error\n");
13091312
return -EFAULT;
13101313
}
@@ -1398,7 +1401,11 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
13981401
kvm_set_pfn_dirty(pfn);
13991402
}
14001403
clean_dcache_guest_page(vcpu, pfn, PMD_SIZE);
1401-
invalidate_icache_guest_page(vcpu, pfn, PMD_SIZE);
1404+
1405+
if (exec_fault) {
1406+
new_pmd = kvm_s2pmd_mkexec(new_pmd);
1407+
invalidate_icache_guest_page(vcpu, pfn, PMD_SIZE);
1408+
}
14021409

14031410
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
14041411
} else {
@@ -1410,7 +1417,11 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
14101417
mark_page_dirty(kvm, gfn);
14111418
}
14121419
clean_dcache_guest_page(vcpu, pfn, PAGE_SIZE);
1413-
invalidate_icache_guest_page(vcpu, pfn, PAGE_SIZE);
1420+
1421+
if (exec_fault) {
1422+
new_pte = kvm_s2pte_mkexec(new_pte);
1423+
invalidate_icache_guest_page(vcpu, pfn, PAGE_SIZE);
1424+
}
14141425

14151426
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, flags);
14161427
}

0 commit comments

Comments
 (0)