KVM: arm64: nv: Implement FEAT_XNX and FEAT_HAF
Oliver Upton
maintainerv3 cover KVM: arm64: nv: Implement FEAT_XNX and FEAT_HAF
Fixes all the issues Marc found in v2:
v2: https://lore.kernel.org/kvmarm/[email protected]/
v2 -> v3:
- Don't use uninitialized XN value in kvm_pgtable_stage2_relax_perms()
- Pass vcpu pointer directly in the stage-2 PTW
- Ensure LL/SC swap implementation correctly handles compare failures
- Fix SRCU imbalance
Oliver Upton (15):
arm64: Detect FEAT_XNX
KVM: arm64: Add support for FEAT_XNX stage-2 permissions
KVM: arm64: nv: Forward FEAT_XNX permissions to the shadow stage-2
KVM: arm64: Teach ptdump about FEAT_XNX permissions
KVM: arm64: nv: Advertise support for FEAT_XNX
KVM: arm64: Call helper for reading descriptors directly
KVM: arm64: nv: Stop passing vCPU through void ptr in S2 PTW
KVM: arm64: Handle endianness in read helper for emulated PTW
KVM: arm64: nv: Use pgtable definitions in stage-2 walk
KVM: arm64: Add helper for swapping guest descriptor
KVM: arm64: Propagate PTW errors up to AT emulation
KVM: arm64: Implement HW access flag management in stage-1 SW PTW
KVM: arm64: nv: Implement HW access flag management in stage-2 SW PTW
KVM: arm64: nv: Expose hardware access flag management to NV guests
KVM: arm64: selftests: Add test for AT emulation
arch/arm64/include/asm/kvm_asm.h | 6 +- arch/arm64/include/asm/kvm_nested.h | 40 +++- arch/arm64/include/asm/kvm_pgtable.h | 17 +- arch/arm64/kernel/cpufeature.c | 7 + arch/arm64/kvm/at.c | 186 ++++++++++++++++-- arch/arm64/kvm/hyp/pgtable.c | 58 +++++- arch/arm64/kvm/mmu.c | 28 ++- arch/arm64/kvm/nested.c | 126 ++++++++---- arch/arm64/kvm/ptdump.c | 35 +++- arch/arm64/kvm/sys_regs.c | 9 +- arch/arm64/tools/cpucaps | 1 + tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/arm64/at.c | 172 ++++++++++++++++ .../testing/selftests/kvm/include/kvm_util.h | 1 + tools/testing/selftests/kvm/lib/kvm_util.c | 10 + 15 files changed, 602 insertions(+), 95 deletions(-) create mode 100644 tools/testing/selftests/kvm/arm64/at.c
base-commit: dcb6fa37fd7bc9c3d2b066329b0d27dedf8becaa
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 1/15 arm64: Detect FEAT_XNX
Detect the feature in anticipation of using it in KVM.
--- arch/arm64/kernel/cpufeature.c | 7 +++++++ arch/arm64/tools/cpucaps | 1 + 2 files changed, 8 insertions(+)
arch/arm64/kernel/cpufeature.c +7 -0
@@ -3088,6 +3088,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .capability = ARM64_HAS_GICV5_LEGACY, .matches = test_has_gicv5_legacy, }, + { + .desc = "XNX", + .capability = ARM64_HAS_XNX, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, +ARM64_CPUID_FIELDS (ID_AA64MMFR1_EL1 ,XNX ,IMP ) + }, {}, };
arch/arm64/tools/cpucaps +1 -0
@@ -64,6 +64,7 @@ HAS_TLB_RANGE HAS_VA52 HAS_VIRT_HOST_EXTN HAS_WFXT +HAS_XNX HAFT HW_DBM KVM_HVHE
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 2/15 KVM: arm64: Add support for FEAT_XNX stage-2 permissions
In reply to Oliver UptonFEAT_XNX adds support for encoding separate execute permissions for EL0 and EL1 at stage-2. Add support for this to the page table library, hiding the unintuitive encoding scheme behind generic pX and uX permission flags.
--- arch/arm64/include/asm/kvm_pgtable.h | 17 ++++---- arch/arm64/kvm/hyp/pgtable.c | 58 ++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 15 deletions(-)
arch/arm64/include/asm/kvm_pgtable.h +10 -7
@@ -89,7 +89,7 @@ typedef u64 kvm_pte_t;#define KVM_PTE_LEAF_ATTR_HI_S1_XN BIT(54) -#define KVM_PTE_LEAF_ATTR_HI_S2_XN BIT(54) +#define KVM_PTE_LEAF_ATTR_HI_S2_XN GENMASK(54, 53)#define KVM_PTE_LEAF_ATTR_HI_S1_GP BIT(50)
@@ -251,12 +251,15 @@ enum kvm_pgtable_stage2_flags {* @KVM_PGTABLE_PROT_SW3: Software bit 3. */ enum kvm_pgtable_prot { -KVM_PGTABLE_PROT_X =BIT (0 ), -KVM_PGTABLE_PROT_W =BIT (1 ), -KVM_PGTABLE_PROT_R =BIT (2 ), - -KVM_PGTABLE_PROT_DEVICE =BIT (3 ), -KVM_PGTABLE_PROT_NORMAL_NC =BIT (4 ), +KVM_PGTABLE_PROT_PX =BIT (0 ), +KVM_PGTABLE_PROT_UX =BIT (1 ), +KVM_PGTABLE_PROT_X =KVM_PGTABLE_PROT_PX | +KVM_PGTABLE_PROT_UX , +KVM_PGTABLE_PROT_W =BIT (2 ), +KVM_PGTABLE_PROT_R =BIT (3 ), + +KVM_PGTABLE_PROT_DEVICE =BIT (4 ), +KVM_PGTABLE_PROT_NORMAL_NC =BIT (5 ),KVM_PGTABLE_PROT_SW0 =BIT (55 ),KVM_PGTABLE_PROT_SW1 =BIT (56 ),
arch/arm64/kvm/hyp/pgtable.c +50 -8
94 lines not on this page · raw message
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 3/15 KVM: arm64: nv: Forward FEAT_XNX permissions to the shadow stage-2
In reply to Oliver UptonAdd support for FEAT_XNX to shadow stage-2 MMUs, being careful to only evaluate XN[0] when the feature is actually exposed to the VM. Restructure the layering of permissions in the fault handler to assume pX and uX then restricting based on the guest's stage-2 afterwards.
--- arch/arm64/include/asm/kvm_nested.h | 37 +++++++++++++++++++++++++++-- arch/arm64/kvm/mmu.c | 23 ++++++++++++++---- arch/arm64/kvm/nested.c | 5 +++- 3 files changed, 57 insertions(+), 8 deletions(-)
arch/arm64/include/asm/kvm_nested.h +35 -2
@@ -120,9 +120,42 @@ static inline bool kvm_s2_trans_writable(struct kvm_s2_trans *trans)return trans->writable; } -static inline bool kvm_s2_trans_executable (struct kvm_s2_trans *trans) +static inline bool kvm_has_xnx (struct kvm *kvm) { -return !(trans->desc &BIT (54 )); +return cpus_have_final_cap (ARM64_HAS_XNX ) && +kvm_has_feat (kvm,ID_AA64MMFR1_EL1 ,XNX ,IMP ); +} + +static inline bool kvm_s2_trans_exec_el0 (struct kvm *kvm,struct kvm_s2_trans *trans) +{ +u8 xn =FIELD_GET (KVM_PTE_LEAF_ATTR_HI_S2_XN , trans->desc); + +if (!kvm_has_xnx (kvm)) + xn &=FIELD_PREP (KVM_PTE_LEAF_ATTR_HI_S2_XN ,0b10 ); + +switch (xn) { +case 0b00 : +case 0b01 : +return true; +default : +return false; + } +} + +static inline bool kvm_s2_trans_exec_el1 (struct kvm *kvm,struct kvm_s2_trans *trans) +{ +u8 xn =FIELD_GET (KVM_PTE_LEAF_ATTR_HI_S2_XN , trans->desc); + +if (!kvm_has_xnx (kvm)) + xn &=FIELD_PREP (KVM_PTE_LEAF_ATTR_HI_S2_XN ,0b10 ); + +switch (xn) { +case 0b00 : +case 0b11 : +return true; +default : +return false; + } } extern int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,
arch/arm64/kvm/mmu.c +18 -5
@@ -1521,6 +1521,16 @@ static void adjust_nested_fault_perms(struct kvm_s2_trans *nested, *prot |=kvm_encode_nested_level (nested); } +static void adjust_nested_exec_perms (struct kvm *kvm, +struct kvm_s2_trans *nested, +enum kvm_pgtable_prot *prot) +{ +if (!kvm_s2_trans_exec_el0 (kvm, nested)) + *prot &= ~KVM_PGTABLE_PROT_UX ; +if (!kvm_s2_trans_exec_el1 (kvm, nested)) + *prot &= ~KVM_PGTABLE_PROT_PX ; +} +#define KVM_PGTABLE_WALK_MEMABORT_FLAGS (KVM_PGTABLE_WALK_HANDLE_FAULT | KVM_PGTABLE_WALK_SHARED) static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
@@ -1572,11 +1582,12 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,if (writable) prot |=KVM_PGTABLE_PROT_W ; -if (exec_fault || - (cpus_have_final_cap (ARM64_HAS_CACHE_DIC ) && - (!nested ||kvm_s2_trans_executable (nested)))) +if (exec_fault ||cpus_have_final_cap (ARM64_HAS_CACHE_DIC )) prot |=KVM_PGTABLE_PROT_X ; +if (nested) +adjust_nested_exec_perms (kvm, nested, &prot); +kvm_fault_lock (kvm);if (mmu_invalidate_retry (kvm, mmu_seq)) { ret = -EAGAIN ;
@@ -1851,11 +1862,13 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, prot |=KVM_PGTABLE_PROT_NORMAL_NC ; else prot |= KVM_PGTABLE_PROT_DEVICE; - }else if (cpus_have_final_cap (ARM64_HAS_CACHE_DIC ) && - (!nested ||kvm_s2_trans_executable (nested))) { + }else if (cpus_have_final_cap (ARM64_HAS_CACHE_DIC )) { prot |=KVM_PGTABLE_PROT_X ; } +if (nested) +adjust_nested_exec_perms (kvm, nested, &prot); +/* * Under the premise of getting a FSC_PERM fault, we just need to relax * permissions only if vma_pagesize equals fault_granule. Otherwise,
arch/arm64/kvm/nested.c +4 -1
@@ -788,7 +788,10 @@ int kvm_s2_handle_perm_fault(struct kvm_vcpu *vcpu, struct kvm_s2_trans *trans)return 0 ;if (kvm_vcpu_trap_is_iabt (vcpu)) { - forward_fault = !kvm_s2_trans_executable (trans); +if (vcpu_mode_priv (vcpu)) + forward_fault = !kvm_s2_trans_exec_el1 (vcpu->kvm, trans); +else + forward_fault = !kvm_s2_trans_exec_el0 (vcpu->kvm, trans); }else {bool write_fault =kvm_is_write_fault (vcpu);
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 4/15 KVM: arm64: Teach ptdump about FEAT_XNX permissions
In reply to Oliver UptonAlthough KVM doesn't make direct use of the feature, guest hypervisors can use FEAT_XNX which influences the permissions of the shadow stage-2. Update ptdump to separately print the privileged and unprivileged execute permissions.
--- arch/arm64/kvm/ptdump.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-)
arch/arm64/kvm/ptdump.c +27 -8
@@ -31,27 +31,46 @@ static const struct ptdump_prot_bits stage2_pte_bits[] = { .val = PTE_VALID, .set = " ", .clear = "F", - }, { + }, + { .mask = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R, .val = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R, .set = "R", .clear = " ", - }, { + }, + { .mask = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W, .val = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W, .set = "W", .clear = " ", - }, { + }, + { .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, - .val = KVM_PTE_LEAF_ATTR_HI_S2_XN, - .set = "NX", - .clear = "x ", - }, { + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b00), + .set = "px ux ", + }, + { + .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b01), + .set = "PXNux ", + }, + { + .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b10), + .set = "PXNUXN", + }, + { + .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b11), + .set = "px UXN", + }, + { .mask = KVM_PTE_LEAF_ATTR_LO_S2_AF, .val = KVM_PTE_LEAF_ATTR_LO_S2_AF, .set = "AF", .clear = " ", - }, { + }, + { .mask = PMD_TYPE_MASK, .val = PMD_TYPE_SECT, .set = "BLK",
Signature · 1 line
2.47.3
Re: [PATCH v3 04/15] KVM: arm64: Teach ptdump about FEAT_XNX permissions
Hi Oliver,
Quoted 72 lines · Oliver Upton
Although KVM doesn't make direct use of the feature, guest hypervisors can use
FEAT_XNXwhich influences the permissions of the shadow stage-2. Update ptdump to separately print the privileged and unprivileged execute permissions.Signed-off-by Oliver Upton <[email protected]>--- arch/arm64/kvm/ptdump.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-)arch/arm64/kvm/ptdump.c +27
-8@@ -31,27 +31,46 @@ static const struct ptdump_prot_bits stage2_pte_bits[] = { .val = PTE_VALID, .set = " ", .clear = "F", - }, { + }, + { .mask = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R, .val = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R, .set = "R", .clear = " ", - }, { + }, + { .mask = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W, .val = KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W, .set = "W", .clear = " ", - }, { + }, + { .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, - .val = KVM_PTE_LEAF_ATTR_HI_S2_XN, - .set = "NX", - .clear = "x ", - }, { + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b00), + .set = "px ux ", + }, + { + .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b01), + .set = "PXNux ", + }, + { + .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b10), + .set = "PXNUXN", + }, + { + .mask = KVM_PTE_LEAF_ATTR_HI_S2_XN, + .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b11), + .set = "px UXN", + }, + { .mask = KVM_PTE_LEAF_ATTR_LO_S2_AF, .val = KVM_PTE_LEAF_ATTR_LO_S2_AF, .set = "AF", .clear = " ", - }, { + }, + { .mask = PMD_TYPE_MASK, .val = PMD_TYPE_SECT, .set = "BLK",Signature · 1 line
2.47.3
This change as commit 81e9fe5c6f25 ("KVM: arm64: Teach ptdump about FEAT_XNX permissions") in -next does not build for me.
GCC 15.2.0
In file included from arch/arm64/include/asm/sysreg.h:1155,
from arch/arm64/include/asm/memory.h:219,
from arch/arm64/include/asm/thread_info.h:17,
from include/linux/thread_info.h:60,
from arch/arm64/include/asm/preempt.h:5,
from include/linux/preempt.h:79,
from include/linux/spinlock.h:56,
from include/linux/wait.h:9,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:7,
from include/linux/debugfs.h:15,
from arch/arm64/kvm/ptdump.c:9:
include/linux/bitfield.h:114:9: error: braced-group within expression allowed only inside a function
114 | ({ \
| ^
arch/arm64/kvm/ptdump.c:49:27: note: in expansion of macro 'FIELD_PREP'
49 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b00),
| ^~~~~~~~~~
include/linux/bitfield.h:114:9: error: braced-group within expression allowed only inside a function
114 | ({ \
| ^
arch/arm64/kvm/ptdump.c:54:27: note: in expansion of macro 'FIELD_PREP'
54 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b01),
| ^~~~~~~~~~
include/linux/bitfield.h:114:9: error: braced-group within expression allowed only inside a function
114 | ({ \
| ^
arch/arm64/kvm/ptdump.c:59:27: note: in expansion of macro 'FIELD_PREP'
59 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b10),
| ^~~~~~~~~~
include/linux/bitfield.h:114:9: error: braced-group within expression allowed only inside a function
114 | ({ \
| ^
arch/arm64/kvm/ptdump.c:64:27: note: in expansion of macro 'FIELD_PREP'
64 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b11),
| ^~~~~~~~~~clang 21.1.6:
arch/arm64/kvm/ptdump.c:49:10: error: statement expression not allowed at file scope
49 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b00),
| ^
include/linux/bitfield.h:114:2: note: expanded from macro 'FIELD_PREP'
114 | ({ \
| ^
arch/arm64/kvm/ptdump.c:54:10: error: statement expression not allowed at file scope
54 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b01),
| ^
include/linux/bitfield.h:114:2: note: expanded from macro 'FIELD_PREP'
114 | ({ \
| ^
arch/arm64/kvm/ptdump.c:59:10: error: statement expression not allowed at file scope
59 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b10),
| ^
include/linux/bitfield.h:114:2: note: expanded from macro 'FIELD_PREP'
114 | ({ \
| ^
arch/arm64/kvm/ptdump.c:64:10: error: statement expression not allowed at file scope
64 | .val = FIELD_PREP(KVM_PTE_LEAF_ATTR_HI_S2_XN, 0b11),
| ^
include/linux/bitfield.h:114:2: note: expanded from macro 'FIELD_PREP'
114 | ({ \
| ^Cheers,
Nathan
Oliver Upton
maintainerPATCH v3 5/15 KVM: arm64: nv: Advertise support for FEAT_XNX
In reply to Oliver UptonEverything is in place to support FEAT_XNX, advertise support.
--- arch/arm64/kvm/nested.c | 1 - 1 file changed, 1 deletion(-)
arch/arm64/kvm/nested.c +0 -1
@@ -1559,7 +1559,6 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val) val &= ~(ID_AA64MMFR1_EL1_CMOW | ID_AA64MMFR1_EL1_nTLBPA |ID_AA64MMFR1_EL1_ETS | -ID_AA64MMFR1_EL1_XNX |ID_AA64MMFR1_EL1_HAFDBS );/* FEAT_E2H0 implies no VHE */ if (test_bit (KVM_ARM_VCPU_HAS_EL2_E2H0 , kvm->arch.vcpu_features))
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 6/15 KVM: arm64: Call helper for reading descriptors directly
In reply to Oliver UptonGoing through a function pointer doesn't serve much purpose when there's only one implementation.
--- arch/arm64/kvm/nested.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-)
arch/arm64/kvm/nested.c +8 -10
@@ -124,7 +124,6 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu) }struct s2_walk_info { -int (*read_desc)(phys_addr_t pa,u64 *desc,void *data);void *data;u64 baddr;unsigned int max_oa_bits;
@@ -199,6 +198,13 @@ static int check_output_size(struct s2_walk_info *wi, phys_addr_t output)return 0 ; } +static int read_guest_s2_desc (phys_addr_t pa,u64 *desc,void *data) +{ +struct kvm_vcpu *vcpu = data; + +return kvm_read_guest (vcpu->kvm, pa, desc,sizeof (*desc)); +} +/* * This is essentially a C-version of the pseudo code from the ARM ARM * AArch64.TranslationTableWalk function. I strongly recommend looking at
@@ -257,7 +263,7 @@ static int walk_nested_s2_pgd(phys_addr_t ipa, >> (addr_bottom - 3); paddr = base_addr | index; - ret = wi->read_desc (paddr, &desc, wi->data); + ret =read_guest_s2_desc (paddr, &desc, wi->data);if (ret <0 )return ret;
@@ -325,13 +331,6 @@ static int walk_nested_s2_pgd(phys_addr_t ipa,return 0 ; } -static int read_guest_s2_desc (phys_addr_t pa,u64 *desc,void *data) -{ -struct kvm_vcpu *vcpu = data; - -return kvm_read_guest (vcpu->kvm, pa, desc,sizeof (*desc)); -} -static void vtcr_to_walk_info (u64 vtcr,struct s2_walk_info *wi) { wi->t0sz = vtcr &TCR_EL2_T0SZ_MASK ;
@@ -364,7 +363,6 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,if (!vcpu_has_nv (vcpu))return 0 ; - wi.read_desc = read_guest_s2_desc; wi.data = vcpu; wi.baddr =vcpu_read_sys_reg (vcpu,VTTBR_EL2 );
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 7/15 KVM: arm64: nv: Stop passing vCPU through void ptr in S2 PTW
In reply to Oliver UptonThe stage-2 table walker passes down the vCPU as a void pointer. That might've made sense if the walker was generic although at this point it is clear this will only ever be used in the context of a vCPU.
--- arch/arm64/kvm/nested.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
arch/arm64/kvm/nested.c +4 -8
@@ -124,7 +124,6 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu) }struct s2_walk_info { -void *data;u64 baddr;unsigned int max_oa_bits;unsigned int pgshift;
@@ -198,10 +197,8 @@ static int check_output_size(struct s2_walk_info *wi, phys_addr_t output)return 0 ; } -static int read_guest_s2_desc (phys_addr_t pa,u64 *desc,void *data) +static int read_guest_s2_desc (struct kvm_vcpu *vcpu,phys_addr_t pa,u64 *desc) { -struct kvm_vcpu *vcpu = data; -return kvm_read_guest (vcpu->kvm, pa, desc,sizeof (*desc)); }
@@ -212,7 +209,7 @@ static int read_guest_s2_desc(phys_addr_t pa, u64 *desc, void *data)* * Must be called with the kvm->srcu read lock held */ -static int walk_nested_s2_pgd (phys_addr_t ipa, +static int walk_nested_s2_pgd (struct kvm_vcpu *vcpu,phys_addr_t ipa,struct s2_walk_info *wi,struct kvm_s2_trans *out) {int first_block_level, level, stride, input_size, base_lower_bound;
@@ -263,7 +260,7 @@ static int walk_nested_s2_pgd(phys_addr_t ipa, >> (addr_bottom - 3); paddr = base_addr | index; - ret =read_guest_s2_desc (paddr, &desc, wi->data); + ret =read_guest_s2_desc (vcpu, paddr, &desc);if (ret <0 )return ret;
@@ -363,14 +360,13 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,if (!vcpu_has_nv (vcpu))return 0 ; - wi.data = vcpu; wi.baddr =vcpu_read_sys_reg (vcpu,VTTBR_EL2 );vtcr_to_walk_info (vtcr, &wi); wi.be =vcpu_read_sys_reg (vcpu,SCTLR_EL2 ) & SCTLR_ELx_EE; - ret =walk_nested_s2_pgd (gipa, &wi, result); + ret =walk_nested_s2_pgd (vcpu, gipa, &wi, result);if (ret) result->esr |= (kvm_vcpu_get_esr (vcpu) & ~ESR_ELx_FSC);
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 8/15 KVM: arm64: Handle endianness in read helper for emulated PTW
In reply to Oliver UptonImplementing FEAT_HAFDBS means adding another descriptor accessor that needs to deal with the guest-configured endianness. Prepare by moving the endianness handling into the read accessor and out of the main body of the S1/S2 PTWs.
--- arch/arm64/kvm/at.c | 25 +++++++++++++++++++------ arch/arm64/kvm/nested.c | 32 ++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 18 deletions(-)
arch/arm64/kvm/at.c +19 -6
@@ -362,6 +362,24 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,return -EFAULT ; } +static int kvm_read_s1_desc (struct kvm_vcpu *vcpu,u64 pa,u64 *desc, +struct s1_walk_info *wi) +{ +u64 val; +int r; + + r =kvm_read_guest (vcpu->kvm, pa, &val,sizeof (val)); +if (r) +return r; + +if (wi->be) + *desc =be64_to_cpu ((__force __be64 )val); +else + *desc =le64_to_cpu ((__force __le64 )val); + +return 0 ; +} +static int walk_s1 (struct kvm_vcpu *vcpu,struct s1_walk_info *wi,struct s1_walk_result *wr,u64 va) {
@@ -414,17 +432,12 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,return ret; } - ret =kvm_read_guest (vcpu->kvm, ipa, &desc,sizeof (desc)); + ret =kvm_read_s1_desc (vcpu, ipa, &desc, wi);if (ret) {fail_s1_walk (wr,ESR_ELx_FSC_SEA_TTW (level), false);return ret; } -if (wi->be) - desc =be64_to_cpu ((__force __be64 )desc); -else - desc =le64_to_cpu ((__force __le64 )desc); -/* Invalid descriptor */ if (!(desc &BIT (0 ))) goto transfault;
arch/arm64/kvm/nested.c +20 -12
@@ -197,9 +197,26 @@ static int check_output_size(struct s2_walk_info *wi, phys_addr_t output)return 0 ; } -static int read_guest_s2_desc (struct kvm_vcpu *vcpu,phys_addr_t pa,u64 *desc) +static int read_guest_s2_desc (struct kvm_vcpu *vcpu,phys_addr_t pa,u64 *desc, +struct s2_walk_info *wi) { -return kvm_read_guest (vcpu->kvm, pa, desc,sizeof (*desc)); +u64 val; +int r; + + r =kvm_read_guest (vcpu->kvm, pa, &val,sizeof (val)); +if (r) +return r; + +/* +* Handle reversedescriptors if endianness differs between the +* host and the guest hypervisor. +*/ +if (wi->be) + *desc =be64_to_cpu ((__force __be64 )val); +else + *desc =le64_to_cpu ((__force __le64 )val); + +return 0 ; }/*
@@ -260,19 +277,10 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa, >> (addr_bottom - 3); paddr = base_addr | index; - ret =read_guest_s2_desc (vcpu, paddr, &desc); + ret =read_guest_s2_desc (vcpu, paddr, &desc, wi);if (ret <0 )return ret; -/* -* Handle reversedescriptors if endianness differs between the -* host and the guest hypervisor. -*/ -if (wi->be) - desc =be64_to_cpu ((__force __be64 )desc); -else - desc =le64_to_cpu ((__force __le64 )desc); -/* Check for valid descriptor at this point */ if (!(desc &1 ) || ((desc &3 ) ==1 && level ==3 )) { out->esr =compute_fsc (level, ESR_ELx_FSC_FAULT);
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 9/15 KVM: arm64: nv: Use pgtable definitions in stage-2 walk
In reply to Oliver UptonUse the existing page table definitions instead of magic numbers for the stage-2 table walk.
--- arch/arm64/kvm/nested.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-)
arch/arm64/kvm/nested.c +15 -6
@@ -282,14 +282,23 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,return ret;/* Check for valid descriptor at this point */ -if (!(desc &1 ) || ((desc &3 ) ==1 && level ==3 )) { +if (!(desc &KVM_PTE_VALID )) { out->esr =compute_fsc (level, ESR_ELx_FSC_FAULT); out->desc = desc;return 1 ; } -/* We're at the final level or block translation level */ -if ((desc &3 ) ==1 || level ==3 ) +if (FIELD_GET (KVM_PTE_TYPE , desc) ==KVM_PTE_TYPE_BLOCK ) { +if (level <3 ) +break ; + + out->esr =compute_fsc (level, ESR_ELx_FSC_FAULT); + out->desc = desc; +return 1 ; + } + +/* We're at the final level */ +if (level ==3 )break ;if (check_output_size (wi, desc)) {
@@ -316,7 +325,7 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,return 1 ; } -if (!(desc &BIT (10 ))) { +if (!(desc &KVM_PTE_LEAF_ATTR_LO_S2_AF )) { out->esr =compute_fsc (level, ESR_ELx_FSC_ACCESS); out->desc = desc;return 1 ;
@@ -329,8 +338,8 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa, (ipa &GENMASK_ULL (addr_bottom -1 ,0 )); out->output = paddr; out->block_size =1UL << ((3 - level) * stride + wi->pgshift); - out->readable = desc & (0b01 <<6 ); - out->writable = desc & (0b10 <<6 ); + out->readable = desc &KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R ; + out->writable = desc &KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W ; out->level = level; out->desc = desc;return 0 ;
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 10/15 KVM: arm64: Add helper for swapping guest descriptor
In reply to Oliver UptonImplementing FEAT_HAFDBS in KVM's software PTWs requires the ability to CAS a descriptor to update the in-memory value. Add an accessor to do exactly that, coping with the fact that guest descriptors are in user memory (duh).
While FEAT_LSE required on any system that implements NV, KVM now uses the stage-1 PTW for non-nested use cases meaning an LL/SC implementation is necessary as well.
--- arch/arm64/include/asm/kvm_nested.h | 2 + arch/arm64/kvm/at.c | 87 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+)
arch/arm64/include/asm/kvm_nested.h +2 -0
@@ -403,4 +403,6 @@ void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val); (FIX_VNCR - __c); \ }) +int __kvm_at_swap_desc (struct kvm *kvm,gpa_t ipa,u64 old,u64 new); +#endif /* __ARM64_KVM_NESTED_H */
arch/arm64/kvm/at.c +87 -0
90 lines not on this page · raw message
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 11/15 KVM: arm64: Propagate PTW errors up to AT emulation
In reply to Oliver UptonKVM's software PTW will soon support 'hardware' updates to the access flag. Similar to fault handling, races to update the descriptor will be handled by restarting the instruction. Prepare for this by propagating errors up to the AT emulation, only retiring the instruction if the walk succeeds.
--- arch/arm64/include/asm/kvm_asm.h | 6 ++--- arch/arm64/kvm/at.c | 43 ++++++++++++++++++++++---------- arch/arm64/kvm/sys_regs.c | 9 ++++--- 3 files changed, 39 insertions(+), 19 deletions(-)
arch/arm64/include/asm/kvm_asm.h +3 -3
@@ -246,9 +246,9 @@ extern void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu);extern int __kvm_tlbi_s1e2 (struct kvm_s2_mmu *mmu,u64 va,u64 sys_encoding);extern void __kvm_timer_set_cntvoff (u64 cntvoff); -extern void __kvm_at_s1e01 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr); -extern void __kvm_at_s1e2 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr); -extern void __kvm_at_s12 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr); +extern int __kvm_at_s1e01 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr); +extern int __kvm_at_s1e2 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr); +extern int __kvm_at_s12 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr);extern int __kvm_vcpu_run (struct kvm_vcpu *vcpu);
arch/arm64/kvm/at.c +30 -13
120 lines not on this page · raw message
arch/arm64/kvm/sys_regs.c +6 -3
@@ -3767,7 +3767,8 @@ static bool handle_at_s1e01(struct kvm_vcpu *vcpu, struct sys_reg_params *p, {u32 op =sys_insn (p->Op0, p->Op1, p->CRn, p->CRm, p->Op2); -__kvm_at_s1e01 (vcpu, op, p->regval); +if (__kvm_at_s1e01 (vcpu, op, p->regval)) +return false;return true; }
@@ -3784,7 +3785,8 @@ static bool handle_at_s1e2(struct kvm_vcpu *vcpu, struct sys_reg_params *p,return false; } -__kvm_at_s1e2 (vcpu, op, p->regval); +if (__kvm_at_s1e2 (vcpu, op, p->regval)) +return false;return true; }
@@ -3794,7 +3796,8 @@ static bool handle_at_s12(struct kvm_vcpu *vcpu, struct sys_reg_params *p, {u32 op =sys_insn (p->Op0, p->Op1, p->CRn, p->CRm, p->Op2); -__kvm_at_s12 (vcpu, op, p->regval); +if (__kvm_at_s12 (vcpu, op, p->regval)) +return false;return true; }
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 12/15 KVM: arm64: Implement HW access flag management in stage-1 SW PTW
In reply to Oliver UptonAtomically update the Access flag at stage-1 when the guest has configured the MMU to do so. Make the implementation choice (and liberal interpretation of speculation) that any access type updates the Access flag, including AT and CMO instructions.
Restart the entire walk by returning to the exception-generating instruction in the case of a failed Access flag update.
--- arch/arm64/include/asm/kvm_nested.h | 1 + arch/arm64/kvm/at.c | 33 +++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-)
arch/arm64/include/asm/kvm_nested.h +1 -0
@@ -353,6 +353,7 @@ struct s1_walk_info {bool be;bool s2;bool pa52bit; +bool ha; };struct s1_walk_result {
arch/arm64/kvm/at.c +31 -2
@@ -346,6 +346,8 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, wi->baddr &=GENMASK_ULL (wi->max_oa_bits -1 , x); + wi->ha = tcr &TCR_HA ; +return 0 ; addrsz:
@@ -380,10 +382,24 @@ static int kvm_read_s1_desc(struct kvm_vcpu *vcpu, u64 pa, u64 *desc,return 0 ; } +static int kvm_swap_s1_desc (struct kvm_vcpu *vcpu,u64 pa,u64 old,u64 new, +struct s1_walk_info *wi) +{ +if (wi->be) { + old =cpu_to_be64 (old); + new =cpu_to_be64 (new); + }else { + old =cpu_to_le64 (old); + new =cpu_to_le64 (new); + } + +return __kvm_at_swap_desc (vcpu->kvm, pa, old, new); +} +static int walk_s1 (struct kvm_vcpu *vcpu,struct s1_walk_info *wi,struct s1_walk_result *wr,u64 va) { -u64 va_top, va_bottom, baddr, desc; +u64 va_top, va_bottom, baddr, desc, new_desc, ipa;int level, stride, ret; level = wi->sl;
@@ -393,7 +409,7 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, va_top =get_ia_size (wi) -1 ;while (1 ) { -u64 index, ipa; +u64 index; va_bottom = (3 - level) * stride + wi->pgshift; index = (va &GENMASK_ULL (va_top, va_bottom)) >> (va_bottom -3 );
@@ -438,6 +454,8 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,return ret; } + new_desc = desc; +/* Invalid descriptor */ if (!(desc &BIT (0 ))) goto transfault;
@@ -490,6 +508,17 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,if (check_output_size (baddr &GENMASK (52 , va_bottom), wi)) goto addrsz; +if (wi->ha) + new_desc |=PTE_AF ; + +if (new_desc != desc) { + ret =kvm_swap_s1_desc (vcpu, ipa, desc, new_desc, wi); +if (ret) +return ret; + + desc = new_desc; + } +if (!(desc &PTE_AF )) {fail_s1_walk (wr,ESR_ELx_FSC_ACCESS_L (level), false);return -EACCES ;
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 13/15 KVM: arm64: nv: Implement HW access flag management in stage-2 SW PTW
In reply to Oliver UptonGive the stage-2 walk similar treatment to stage-1: update the access flag during the table walk and do so for any walk context.
--- arch/arm64/kvm/mmu.c | 5 +++++ arch/arm64/kvm/nested.c | 44 ++++++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 7 deletions(-)
arch/arm64/kvm/mmu.c +5 -0
@@ -2012,6 +2012,11 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)u32 esr; ret =kvm_walk_nested_s2 (vcpu, fault_ipa, &nested_trans); +if (ret == -EAGAIN ) { + ret =1 ; + goto out_unlock; + } +if (ret) { esr =kvm_s2_trans_esr (&nested_trans);kvm_inject_s2_fault (vcpu, esr);
arch/arm64/kvm/nested.c +37 -7
80 lines not on this page · raw message
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 14/15 KVM: arm64: nv: Expose hardware access flag management to NV guests
In reply to Oliver UptonEverything is in place to update the access flag at S1 and S2. Expose support for the access flag flavor of FEAT_HAFDBS to NV guests.
--- arch/arm64/kvm/nested.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
arch/arm64/kvm/nested.c +4 -2
@@ -1599,11 +1599,13 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val)case SYS_ID_AA64MMFR1_EL1 : val &= ~(ID_AA64MMFR1_EL1_CMOW | ID_AA64MMFR1_EL1_nTLBPA | -ID_AA64MMFR1_EL1_ETS | -ID_AA64MMFR1_EL1_HAFDBS ); +ID_AA64MMFR1_EL1_ETS ); +/* FEAT_E2H0 implies no VHE */ if (test_bit (KVM_ARM_VCPU_HAS_EL2_E2H0 , kvm->arch.vcpu_features)) val &= ~ID_AA64MMFR1_EL1_VH ; + + val =ID_REG_LIMIT_FIELD_ENUM (val,ID_AA64MMFR1_EL1 ,HAFDBS ,AF );break ;case SYS_ID_AA64MMFR2_EL1 :
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH v3 15/15 KVM: arm64: selftests: Add test for AT emulation
In reply to Oliver UptonAdd a basic test for AT emulation in the EL2&0 and EL1&0 translation regimes.
--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/arm64/at.c | 166 ++++++++++++++++++ .../testing/selftests/kvm/include/kvm_util.h | 1 + tools/testing/selftests/kvm/lib/kvm_util.c | 10 ++ 4 files changed, 178 insertions(+) create mode 100644 tools/testing/selftests/kvm/arm64/at.c
tools/testing/selftests/kvm/Makefile.kvm +1 -0
@@ -156,6 +156,7 @@ TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test TEST_GEN_PROGS_arm64 = $(TEST_GEN_PROGS_COMMON) TEST_GEN_PROGS_arm64 += arm64/aarch32_id_regs TEST_GEN_PROGS_arm64 += arm64/arch_timer_edge_cases +TEST_GEN_PROGS_arm64 += arm64/at TEST_GEN_PROGS_arm64 += arm64/debug-exceptions TEST_GEN_PROGS_arm64 += arm64/hello_el2 TEST_GEN_PROGS_arm64 += arm64/host_sve
tools/testing/selftests/kvm/arm64/at.c new file +166 -0
166 lines not on this page · raw message
tools/testing/selftests/kvm/include/kvm_util.h +1 -0
@@ -715,6 +715,7 @@ static inline bool vm_arch_has_protected_memory(struct kvm_vm *vm)#endif void vm_mem_region_set_flags (struct kvm_vm *vm,uint32_t slot,uint32_t flags); +void vm_mem_region_reload (struct kvm_vm *vm,uint32_t slot);void vm_mem_region_move (struct kvm_vm *vm,uint32_t slot,uint64_t new_gpa);void vm_mem_region_delete (struct kvm_vm *vm,uint32_t slot);struct kvm_vcpu *__vm_vcpu_add (struct kvm_vm *vm,uint32_t vcpu_id);
tools/testing/selftests/kvm/lib/kvm_util.c +10 -0
@@ -1201,6 +1201,16 @@ void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags) ret, errno, slot, flags); } +void vm_mem_region_reload (struct kvm_vm *vm,uint32_t slot) +{ +struct userspace_mem_region *region =memslot2region (vm, slot); +struct kvm_userspace_memory_region2 tmp = region->region; + + tmp.memory_size =0 ; +vm_ioctl (vm,KVM_SET_USER_MEMORY_REGION2 , &tmp); +vm_ioctl (vm,KVM_SET_USER_MEMORY_REGION2 , ®ion->region); +} +/* * VM Memory Region Move *
Signature · 1 line
2.47.3
Re: [PATCH v3 15/15] KVM: arm64: selftests: Add test for AT emulation
Hi Oliver,
Quoted 126 lines · Oliver Upton
Add a basic test for AT emulation in the EL2&0 and EL1&0 translation regimes.
Signed-off-by Oliver Upton <[email protected]>--- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/arm64/at.c | 166 ++++++++++++++++++ .../testing/selftests/kvm/include/kvm_util.h | 1 + tools/testing/selftests/kvm/lib/kvm_util.c | 10 ++ 4 files changed, 178 insertions(+) create mode 100644 tools/testing/selftests/kvm/arm64/at.ctools/testing/selftests/kvm/Makefile.kvm +1
-0@@ -156,6 +156,7 @@ TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test TEST_GEN_PROGS_arm64 = $(TEST_GEN_PROGS_COMMON) TEST_GEN_PROGS_arm64 += arm64/aarch32_id_regs TEST_GEN_PROGS_arm64 += arm64/arch_timer_edge_cases +TEST_GEN_PROGS_arm64 += arm64/at TEST_GEN_PROGS_arm64 += arm64/debug-exceptions TEST_GEN_PROGS_arm64 += arm64/hello_el2 TEST_GEN_PROGS_arm64 += arm64/host_svetools/testing/selftests/kvm/arm64/at.c new file +96
-0@@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* at - Test for KVM's AT emulation in the EL2&0 and EL1&0 translation regimes. +*/ +#include "kvm_util.h" +#include "processor.h" +#include "test_util.h" +#include "ucall.h" + +#include <asm/sysreg.h> + +#define TEST_ADDR 0x80000000 + +enum { +CLEAR_ACCESS_FLAG , +TEST_ACCESS_FLAG , +}; + +static u64 *ptep_hva; + +#define copy_el2_to_el1 (reg) \ + write_sysreg_s(read_sysreg_s(SYS_##reg##_EL1), SYS_##reg##_EL12) + +/* Yes, this is an ugly hack */ +#define __at (op, addr) write_sysreg_s(addr, op) + +#define test_at_insn (op, expect_fault) \ +do { \ + u64 par, fsc; \ + bool fault; \ + \ + GUEST_SYNC(CLEAR_ACCESS_FLAG); \ + \ + __at(OP_AT_##op, TEST_ADDR); \ + isb(); \ + par = read_sysreg(par_el1); \ + \ + fault = par & SYS_PAR_EL1_F; \ + fsc = FIELD_GET(SYS_PAR_EL1_FST, par); \ + \ + __GUEST_ASSERT((expect_fault) == fault, \ + "AT "#op": %sexpected fault (par: %lx)1", \ + (expect_fault) ? "" : "un", par); \ + if ((expect_fault)) { \ + __GUEST_ASSERT(fsc == ESR_ELx_FSC_ACCESS_L(3), \ + "AT "#op": expected access flag fault (par: %lx)", \ + par); \ + } else { \ + GUEST_ASSERT_EQ(FIELD_GET(SYS_PAR_EL1_ATTR, par), MAIR_ATTR_NORMAL); \ + GUEST_ASSERT_EQ(FIELD_GET(SYS_PAR_EL1_SH, par), PTE_SHARED >> 8); \ + GUEST_ASSERT_EQ(par & SYS_PAR_EL1_PA, TEST_ADDR); \ + GUEST_SYNC(TEST_ACCESS_FLAG); \ + } \ +} while (0) + +static void test_at (bool expect_fault) +{ +test_at_insn (S1E2R , expect_fault); +test_at_insn (S1E2W , expect_fault); + +/* Reuse the stage-1 MMU context from EL2 at EL1 */ +copy_el2_to_el1 (SCTLR ); +copy_el2_to_el1 (MAIR ); +copy_el2_to_el1 (TCR ); +copy_el2_to_el1 (TTBR0 ); +copy_el2_to_el1 (TTBR1 ); + +/* Disable stage-2 translation and enter a non-host context */ +write_sysreg (0 , vtcr_el2); +write_sysreg (0 , vttbr_el2); +sysreg_clear_set (hcr_el2,HCR_EL2_TGE |HCR_EL2_VM ,0 ); +isb (); + +test_at_insn (S1E1R , expect_fault); +test_at_insn (S1E1W , expect_fault); +} + +static void guest_code (void ) +{ +sysreg_clear_set (tcr_el1,TCR_HA ,0 ); +isb (); + +test_at (true); + +if (!SYS_FIELD_GET (ID_AA64MMFR1_EL1 ,HAFDBS ,read_sysreg (id_aa64mmfr1_el1))) +GUEST_DONE (); + +/* +* KVM's software PTW makes the implementation choice that the AT +* instruction sets the access flag. +*/ +sysreg_clear_set (tcr_el1,0 ,TCR_HA ); +isb (); +test_at (false); + +GUEST_DONE ();
tools/testing/selftests/kvm/arm64/at.c line 106
Quoted 10 lines
tools/testing/selftests/kvm/arm64/at.c new file +10
-0+} + +static void handle_sync (struct kvm_vcpu *vcpu,struct ucall *uc) +{ +switch (uc->args[1 ]) { +case CLEAR_ACCESS_FLAG : +/* +* Delete + reinstall the memslot to invalidate stage-2 +* mappings of the stage-1 page tables, forcing KVM to +* use the 'slow' AT emulation path.
Once the guest continues executing instructions, we would get a stage 2 translation fault (on a S1 PTW) immediately and re-build the stage 2 mappings for the S1 page tables. Depends on how much we have re-built for S1 page tables, the "forcing KVM to use the 'slow' AT emulation path" may not always be true.
I tested it in a QEMU guest (with "-machine virt,virtualization=on \
-cpu max -accel tcg", so it's essentially a NV2 capable HW!) and it failed at the first test_at_insn(S1E2R, expect_fault):
[root@localhost arm64]# getconf PAGESIZE
65536
[root@localhost arm64]# ./at
Random seed: 0x6b8b4567__vm_create: mode='PA-bits:40, VA-bits:48, 4K pages' type='0', pages='672' ==== Test Assertion Failure ====
arm64/at.c:58: (expect_fault) == fault
pid=824 tid=824 errno=4 - Interrupted system call
1 0x0000000000402d8f: run_test at at.c:137
2 0x00000000004020e3: main at at.c:162
3 0x0000ffffb07daf3b: ?? ??:0
4 0x0000ffffb07db007: ?? ??:0
5 0x000000000040222f: _start at ??:?AT S1E2R: expected fault (par: ff00000080000b80)1
because in my test, the S1 page tables used to translate TEST_ADDR are located at
level 0: 0x180000 level 1: 0x181000 level 2: 0x187000 level 3: 0x188000
All of them had already been re-mapped in stage 2 [*] (in the same 64KB physical page) before KVM started to emulate AT S1E2R. The AT_S1E1R (fast path) will successfully get the translation result and it's IMP DEF that whether an access flag fault will be triggered (QEMU chooses to not fault). Whatever, [*] is out of expect and result in the failure.
I have no idea that if we can actually force the slow emulation path.
Thanks,
Zenghui
Quoted 93 lines
tools/testing/selftests/kvm/arm64/at.c new file +60
-0+* +* This and clearing the access flag from host userspace +* ensures that the access flag cannot be set speculatively +* and is reliably cleared at the time of the AT instruction. +*/ +clear_bit (__ffs (PTE_AF ), ptep_hva); +vm_mem_region_reload (vcpu->vm, vcpu->vm->memslots[MEM_REGION_PT ]); +break ; +case TEST_ACCESS_FLAG : +TEST_ASSERT (test_bit (__ffs (PTE_AF ), ptep_hva), +"Expected access flag to be set (desc: %lu)" , *ptep_hva); +break ; +default : +TEST_FAIL ("Unexpected SYNC arg: %lu" , uc->args[1 ]); + } +} + +static void run_test (struct kvm_vcpu *vcpu) +{ +struct ucall uc; + +while (true) { +vcpu_run (vcpu); +switch (get_ucall (vcpu, &uc)) { +case UCALL_DONE : +return ; +case UCALL_SYNC : +handle_sync (vcpu, &uc); +continue ; +case UCALL_ABORT : +REPORT_GUEST_ASSERT (uc); +return ; +default : +TEST_FAIL ("Unexpeced ucall: %lu" , uc.cmd); + } + } +} + +int main (void ) +{ +struct kvm_vcpu_init init; +struct kvm_vcpu *vcpu; +struct kvm_vm *vm; + +TEST_REQUIRE (kvm_check_cap (KVM_CAP_ARM_EL2 )); + + vm =vm_create (1 ); + +kvm_get_default_vcpu_target (vm, &init); + init.features[0 ] |=BIT (KVM_ARM_VCPU_HAS_EL2 ); + vcpu =aarch64_vcpu_add (vm,0 , &init, guest_code); +kvm_arch_vm_finalize_vcpus (vm); + +virt_map (vm,TEST_ADDR ,TEST_ADDR ,1 ); + ptep_hva =virt_get_pte_hva_at_level (vm,TEST_ADDR ,3 ); +run_test (vcpu); + +kvm_vm_free (vm); +return 0 ; +}tools/testing/selftests/kvm/include/kvm_util.h +1
-0@@ -715,6 +715,7 @@ static inline bool vm_arch_has_protected_memory(struct kvm_vm *vm)#endif void vm_mem_region_set_flags (struct kvm_vm *vm,uint32_t slot,uint32_t flags); +void vm_mem_region_reload (struct kvm_vm *vm,uint32_t slot);void vm_mem_region_move (struct kvm_vm *vm,uint32_t slot,uint64_t new_gpa);void vm_mem_region_delete (struct kvm_vm *vm,uint32_t slot);struct kvm_vcpu *__vm_vcpu_add (struct kvm_vm *vm,uint32_t vcpu_id);tools/testing/selftests/kvm/lib/kvm_util.c +10
-0@@ -1201,6 +1201,16 @@ void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags) ret, errno, slot, flags); } +void vm_mem_region_reload (struct kvm_vm *vm,uint32_t slot) +{ +struct userspace_mem_region *region =memslot2region (vm, slot); +struct kvm_userspace_memory_region2 tmp = region->region; + + tmp.memory_size =0 ; +vm_ioctl (vm,KVM_SET_USER_MEMORY_REGION2 , &tmp); +vm_ioctl (vm,KVM_SET_USER_MEMORY_REGION2 , ®ion->region); +} +/* * VM Memory Region Move *
Re: [PATCH v3 15/15] KVM: arm64: selftests: Add test for AT emulation
Quoted 45 lines · Zenghui Yu
Hi Oliver,
tools/testing/selftests/kvm/arm64/at.c line 106
Quoted 8 lines · Oliver Upton
tools/testing/selftests/kvm/arm64/at.c new file +8
-0+static void handle_sync (struct kvm_vcpu *vcpu,struct ucall *uc) +{ +switch (uc->args[1 ]) { +case CLEAR_ACCESS_FLAG : +/* +* Delete + reinstall the memslot to invalidate stage-2 +* mappings of the stage-1 page tables, forcing KVM to +* use the 'slow' AT emulation path. Once the guest continues executing instructions, we would get a stage 2 translation fault (on a S1 PTW) immediately and re-build the stage 2 mappings for the S1 page tables. Depends on how much we have re-built for S1 page tables, the "forcing KVM to use the 'slow' AT emulation path" may not always be true.
I tested it in a QEMU guest (with "-machine virt,virtualization=on \
-cpu max -accel tcg", so it's essentially a NV2 capable HW!) and it failed at the firsttest_at_insn(S1E2R, expect_fault):[root@localhost arm64]# getconf PAGESIZE
65536
[root@localhost arm64]# ./at
Random seed:0x6b8b4567__vm_create: mode='PA-bits:40, VA-bits:48, 4K pages' type='0', pages='672' ==== Test Assertion Failure ====
arm64/at.c:58: (expect_fault) == fault
pid=824 tid=824 errno=4 - Interrupted system call1 0x0000000000402d8f: run_test at at.c:137 2 0x00000000004020e3: main at at.c:162 3 0x0000ffffb07daf3b: ?? ??:0 4 0x0000ffffb07db007: ?? ??:0 5 0x000000000040222f: _start at ??:?AT S1E2R: expected fault (par: ff00000080000b80)1
because in my test, the S1 page tables used to translate
TEST_ADDRare located atlevel 0: 0x180000 level 1: 0x181000 level 2: 0x187000 level 3: 0x188000
Quoted 5 lines
All of them had already been re-mapped in stage 2 [*] (in the same 64KB physical page) before KVM started to emulate AT S1E2R. The
AT_S1E1R(fast path) will successfully get the translation result and it's IMP DEF that whether an access flag fault will be triggered (QEMU chooses to not fault).
Fix myself up: an access flag fault should be taken in this case (with AF bit is 0 and HA is not enabled). I've just sent a fix to QEMU for that [*].
Given that we can't force the slow emulation path, we should still avoid testing the IMP DEF behavior (i.e., TEST_ACCESS_FLAG). I will send a patch to remove it.
[*] https://lore.kernel.org/r/[email protected]
Thanks,
Zenghui
Quoted 10 lines · Oliver Upton
Fixes all the issues Marc found in v2:
v2: https://lore.kernel.org/kvmarm/[email protected]/
v2 -> v3:
- Don't use uninitialized XN value inkvm_pgtable_stage2_relax_perms()
- Pass vcpu pointer directly in the stage-2 PTW
- Ensure LL/SC swap implementation correctly handles compare failures
- Fix SRCU imbalance
Thanks for the quick respin, this looks pretty good now. I've taken it for a ride on the usual setup, and this time nothing caught fire.
FWIW:
M.
Signature · 1 line
Jazz isn't dead. It just smells funny.
Quoted 11 lines · Oliver Upton
Fixes all the issues Marc found in v2:
v2: https://lore.kernel.org/kvmarm/[email protected]/
v2 -> v3:
- Don't use uninitialized XN value inkvm_pgtable_stage2_relax_perms()
- Pass vcpu pointer directly in the stage-2 PTW
- Ensure LL/SC swap implementation correctly handles compare failures
- Fix SRCU imbalance[...]
Applied to next, thanks!
[01/15] arm64: Detect FEAT_XNX
https://git.kernel.org/kvmarm/kvmarm/c/dc31124379b6 [02/15] KVM: arm64: Add support for FEAT_XNX stage-2 permissions https://git.kernel.org/kvmarm/kvmarm/c/2608563b466b [03/15] KVM: arm64: nv: Forward FEAT_XNX permissions to the shadow stage-2 https://git.kernel.org/kvmarm/kvmarm/c/d93febe2ed2e [04/15] KVM: arm64: Teach ptdump about FEAT_XNX permissions https://git.kernel.org/kvmarm/kvmarm/c/81e9fe5c6f25 [05/15] KVM: arm64: nv: Advertise support for FEAT_XNX https://git.kernel.org/kvmarm/kvmarm/c/5aef6974ec47 [06/15] KVM: arm64: Call helper for reading descriptors directly https://git.kernel.org/kvmarm/kvmarm/c/e218981560f6 [07/15] KVM: arm64: nv: Stop passing vCPU through void ptr in S2 PTW https://git.kernel.org/kvmarm/kvmarm/c/c2c5d3b248e3 [08/15] KVM: arm64: Handle endianness in read helper for emulated PTW https://git.kernel.org/kvmarm/kvmarm/c/a1b8922186f8 [09/15] KVM: arm64: nv: Use pgtable definitions in stage-2 walk https://git.kernel.org/kvmarm/kvmarm/c/0a610d2056c2 [10/15] KVM: arm64: Add helper for swapping guest descriptor https://git.kernel.org/kvmarm/kvmarm/c/9020b38e23db [11/15] KVM: arm64: Propagate PTW errors up to AT emulation https://git.kernel.org/kvmarm/kvmarm/c/15e996ef9dc5 [12/15] KVM: arm64: Implement HW access flag management in stage-1 SW PTW https://git.kernel.org/kvmarm/kvmarm/c/c59ca4b5b0c3 [13/15] KVM: arm64: nv: Implement HW access flag management in stage-2 SW PTW https://git.kernel.org/kvmarm/kvmarm/c/39db933ba67f [14/15] KVM: arm64: nv: Expose hardware access flag management to NV guests https://git.kernel.org/kvmarm/kvmarm/c/6774884fabf0 [15/15] KVM: arm64: selftests: Add test for AT emulation https://git.kernel.org/kvmarm/kvmarm/c/662d0d6cccba
--
Best,
Oliver