Skip to content

Commit 7d89483

Browse files
YananWang-hubMarc Zyngier
authored andcommitted
KVM: arm64: Add usage of stage 2 fault lookup level in user_mem_abort()
If we get a FSC_PERM fault, just using (logging_active && writable) to determine calling kvm_pgtable_stage2_map(). There will be two more cases we should consider. (1) After logging_active is configged back to false from true. When we get a FSC_PERM fault with write_fault and adjustment of hugepage is needed, we should merge tables back to a block entry. This case is ignored by still calling kvm_pgtable_stage2_relax_perms(), which will lead to an endless loop and guest panic due to soft lockup. (2) We use (FSC_PERM && logging_active && writable) to determine collapsing a block entry into a table by calling kvm_pgtable_stage2_map(). But sometimes we may only need to relax permissions when trying to write to a page other than a block. In this condition,using kvm_pgtable_stage2_relax_perms() will be fine. The ISS filed bit[1:0] in ESR_EL2 regesiter indicates the stage2 lookup level at which a D-abort or I-abort occurred. By comparing granule of the fault lookup level with vma_pagesize, we can strictly distinguish conditions of calling kvm_pgtable_stage2_relax_perms() or kvm_pgtable_stage2_map(), and the above two cases will be well considered. Suggested-by: Keqian Zhu <[email protected]> Signed-off-by: Yanan Wang <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Acked-by: Will Deacon <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 3a0b870 commit 7d89483

File tree

3 files changed

+15
-2
lines changed

3 files changed

+15
-2
lines changed

arch/arm64/include/asm/esr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
/* Shared ISS fault status code(IFSC/DFSC) for Data/Instruction aborts */
105105
#define ESR_ELx_FSC (0x3F)
106106
#define ESR_ELx_FSC_TYPE (0x3C)
107+
#define ESR_ELx_FSC_LEVEL (0x03)
107108
#define ESR_ELx_FSC_EXTABT (0x10)
108109
#define ESR_ELx_FSC_SERROR (0x11)
109110
#define ESR_ELx_FSC_ACCESS (0x08)

arch/arm64/include/asm/kvm_emulate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,11 @@ static __always_inline u8 kvm_vcpu_trap_get_fault_type(const struct kvm_vcpu *vc
350350
return kvm_vcpu_get_esr(vcpu) & ESR_ELx_FSC_TYPE;
351351
}
352352

353+
static __always_inline u8 kvm_vcpu_trap_get_fault_level(const struct kvm_vcpu *vcpu)
354+
{
355+
return kvm_vcpu_get_esr(vcpu) & ESR_ELx_FSC_LEVEL;
356+
}
357+
353358
static __always_inline bool kvm_vcpu_abt_issea(const struct kvm_vcpu *vcpu)
354359
{
355360
switch (kvm_vcpu_trap_get_fault(vcpu)) {

arch/arm64/kvm/mmu.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,10 +754,12 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
754754
gfn_t gfn;
755755
kvm_pfn_t pfn;
756756
bool logging_active = memslot_is_logging(memslot);
757-
unsigned long vma_pagesize;
757+
unsigned long fault_level = kvm_vcpu_trap_get_fault_level(vcpu);
758+
unsigned long vma_pagesize, fault_granule;
758759
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
759760
struct kvm_pgtable *pgt;
760761

762+
fault_granule = 1UL << ARM64_HW_PGTABLE_LEVEL_SHIFT(fault_level);
761763
write_fault = kvm_is_write_fault(vcpu);
762764
exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
763765
VM_BUG_ON(write_fault && exec_fault);
@@ -896,7 +898,12 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
896898
else if (cpus_have_const_cap(ARM64_HAS_CACHE_DIC))
897899
prot |= KVM_PGTABLE_PROT_X;
898900

899-
if (fault_status == FSC_PERM && !(logging_active && writable)) {
901+
/*
902+
* Under the premise of getting a FSC_PERM fault, we just need to relax
903+
* permissions only if vma_pagesize equals fault_granule. Otherwise,
904+
* kvm_pgtable_stage2_map() should be called to change block size.
905+
*/
906+
if (fault_status == FSC_PERM && vma_pagesize == fault_granule) {
900907
ret = kvm_pgtable_stage2_relax_perms(pgt, fault_ipa, prot);
901908
} else {
902909
ret = kvm_pgtable_stage2_map(pgt, fault_ipa, vma_pagesize,

0 commit comments

Comments
 (0)