Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1476591 > unrolled thread
| Started by | Punit Agrawal <punit.agrawal@arm.com> |
|---|---|
| First post | 2016-09-05 18:40 +0200 |
| Last post | 2016-09-06 19:00 +0200 |
| Articles | 4 — 2 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
[RFC v2 PATCH 6/7] arm64: KVM: Handle trappable TLB instructions Punit Agrawal <punit.agrawal@arm.com> - 2016-09-05 18:40 +0200
Re: [RFC v2 PATCH 6/7] arm64: KVM: Handle trappable TLB instructions Christoffer Dall <christoffer.dall@linaro.org> - 2016-09-06 12:20 +0200
Re: [RFC v2 PATCH 6/7] arm64: KVM: Handle trappable TLB instructions Punit Agrawal <punit.agrawal@arm.com> - 2016-09-06 17:50 +0200
Re: [RFC v2 PATCH 6/7] arm64: KVM: Handle trappable TLB instructions Christoffer Dall <christoffer.dall@linaro.org> - 2016-09-06 19:00 +0200
| From | Punit Agrawal <punit.agrawal@arm.com> |
|---|---|
| Date | 2016-09-05 18:40 +0200 |
| Subject | [RFC v2 PATCH 6/7] arm64: KVM: Handle trappable TLB instructions |
| Message-ID | <se56a-6cw-23@gated-at.bofh.it> |
The ARMv8 architecture allows trapping of TLB maintenane instructions
from EL0/EL1 to higher exception levels. On encountering a trappable TLB
instruction in a guest, an exception is taken to EL2.
Add functionality to handle emulating the TLB instructions.
Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
Cc: Christoffer Dall <christoffer.dall@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/kvm/hyp/tlb.c | 69 ++++++++++++++++++++++++++++++++++
arch/arm64/kvm/sys_regs.c | 81 ++++++++++++++++++++++++++++++++++++++++
arch/arm64/kvm/trace.h | 16 ++++++++
4 files changed, 167 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 7561f63..1ac1cc3 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -49,6 +49,7 @@ extern char __kvm_hyp_vector[];
extern void __kvm_flush_vm_context(void);
extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
+extern void __kvm_emulate_tlb_invalidate(struct kvm *kvm, u32 sysreg, u64 regval);
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c
index 4cda100..1210f58 100644
--- a/arch/arm64/kvm/hyp/tlb.c
+++ b/arch/arm64/kvm/hyp/tlb.c
@@ -78,3 +78,72 @@ static void __hyp_text __tlb_flush_vm_context(void)
}
__alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
+
+/* Intentionally empty functions */
+static void __hyp_text __switch_to_hyp_role_nvhe(void) { }
+static void __hyp_text __switch_to_host_role_nvhe(void) { }
+
+static void __hyp_text __switch_to_hyp_role_vhe(void)
+{
+ u64 hcr = read_sysreg(hcr_el2);
+
+ hcr &= ~HCR_TGE;
+ write_sysreg(hcr, hcr_el2);
+}
+
+static void __hyp_text __switch_to_host_role_vhe(void)
+{
+ u64 hcr = read_sysreg(hcr_el2);
+
+ hcr |= HCR_TGE;
+ write_sysreg(hcr, hcr_el2);
+}
+
+static hyp_alternate_select(__switch_to_hyp_role,
+ __switch_to_hyp_role_nvhe,
+ __switch_to_hyp_role_vhe,
+ ARM64_HAS_VIRT_HOST_EXTN);
+
+static hyp_alternate_select(__switch_to_host_role,
+ __switch_to_host_role_nvhe,
+ __switch_to_host_role_vhe,
+ ARM64_HAS_VIRT_HOST_EXTN);
+
+static void __hyp_text __switch_to_guest_regime(struct kvm *kvm)
+{
+ write_sysreg(kvm->arch.vttbr, vttbr_el2);
+ __switch_to_hyp_role();
+ isb();
+}
+
+static void __hyp_text __switch_to_host_regime(void)
+{
+ __switch_to_host_role();
+ write_sysreg(0, vttbr_el2);
+}
+
+void __hyp_text
+__kvm_emulate_tlb_invalidate(struct kvm *kvm, u32 sys_op, u64 regval)
+{
+ kvm = kern_hyp_va(kvm);
+
+ /*
+ * Switch to the guest before performing any TLB operations to
+ * target the appropriate VMID
+ */
+ __switch_to_guest_regime(kvm);
+
+ /*
+ * TLB maintenance operations are broadcast to
+ * inner-shareable domain when HCR_FB is set (default for
+ * KVM).
+ *
+ * Nuke all Stage 1 TLB entries for the VM. This will kill
+ * performance but it's always safe to do as we don't leave
+ * behind any strays in the TLB
+ */
+ __tlbi(vmalle1is);
+ isb();
+
+ __switch_to_host_regime();
+}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index e51367d..0e70da9 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -790,6 +790,18 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
return true;
}
+static bool emulate_tlb_invalidate(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ u32 opcode = sys_reg(p->Op0, p->Op1, p->CRn, p->CRm, p->Op2);
+
+ kvm_call_hyp(__kvm_emulate_tlb_invalidate,
+ vcpu->kvm, opcode, p->regval);
+ trace_kvm_tlb_invalidate(*vcpu_pc(vcpu), opcode);
+
+ return true;
+}
+
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
/* DBGBVRn_EL1 */ \
@@ -841,6 +853,35 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{ Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010),
access_dcsw },
+ /*
+ * ARMv8 ARM: Table C5-4 TLB maintenance instructions
+ * (Ref: ARMv8 ARM C5.1 version: ARM DDI 0487A.j)
+ */
+ /* TLBI VMALLE1IS */
+ { Op0(1), Op1(0), CRn(8), CRm(3), Op2(0), emulate_tlb_invalidate },
+ /* TLBI VAE1IS */
+ { Op0(1), Op1(0), CRn(8), CRm(3), Op2(1), emulate_tlb_invalidate },
+ /* TLBI ASIDE1IS */
+ { Op0(1), Op1(0), CRn(8), CRm(3), Op2(2), emulate_tlb_invalidate },
+ /* TLBI VAAE1IS */
+ { Op0(1), Op1(0), CRn(8), CRm(3), Op2(3), emulate_tlb_invalidate },
+ /* TLBI VALE1IS */
+ { Op0(1), Op1(0), CRn(8), CRm(3), Op2(5), emulate_tlb_invalidate },
+ /* TLBI VAALE1IS */
+ { Op0(1), Op1(0), CRn(8), CRm(3), Op2(7), emulate_tlb_invalidate },
+ /* TLBI VMALLE1 */
+ { Op0(1), Op1(0), CRn(8), CRm(7), Op2(0), emulate_tlb_invalidate },
+ /* TLBI VAE1 */
+ { Op0(1), Op1(0), CRn(8), CRm(7), Op2(1), emulate_tlb_invalidate },
+ /* TLBI ASIDE1 */
+ { Op0(1), Op1(0), CRn(8), CRm(7), Op2(2), emulate_tlb_invalidate },
+ /* TLBI VAAE1 */
+ { Op0(1), Op1(0), CRn(8), CRm(7), Op2(3), emulate_tlb_invalidate },
+ /* TLBI VALE1 */
+ { Op0(1), Op1(0), CRn(8), CRm(7), Op2(5), emulate_tlb_invalidate },
+ /* TLBI VAALE1 */
+ { Op0(1), Op1(0), CRn(8), CRm(7), Op2(7), emulate_tlb_invalidate },
+
DBG_BCR_BVR_WCR_WVR_EL1(0),
DBG_BCR_BVR_WCR_WVR_EL1(1),
/* MDCCINT_EL1 */
@@ -1329,6 +1370,46 @@ static const struct sys_reg_desc cp15_regs[] = {
{ Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
{ Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
+ /*
+ * TLB operations
+ */
+ /* TLBIALLIS */
+ { Op1( 0), CRn( 8), CRm( 3), Op2( 0), emulate_tlb_invalidate},
+ /* TLBIMVAIS */
+ { Op1( 0), CRn( 8), CRm( 3), Op2( 1), emulate_tlb_invalidate},
+ /* TLBIASIDIS */
+ { Op1( 0), CRn( 8), CRm( 3), Op2( 2), emulate_tlb_invalidate},
+ /* TLBIMVAAIS */
+ { Op1( 0), CRn( 8), CRm( 3), Op2( 3), emulate_tlb_invalidate},
+ /* TLBIMVALIS */
+ { Op1( 0), CRn( 8), CRm( 3), Op2( 5), emulate_tlb_invalidate},
+ /* TLBIMVAALIS */
+ { Op1( 0), CRn( 8), CRm( 3), Op2( 7), emulate_tlb_invalidate},
+ /* ITLBIALL */
+ { Op1( 0), CRn( 8), CRm( 5), Op2( 0), emulate_tlb_invalidate},
+ /* ITLBIMVA */
+ { Op1( 0), CRn( 8), CRm( 5), Op2( 1), emulate_tlb_invalidate},
+ /* ITLBIASID */
+ { Op1( 0), CRn( 8), CRm( 5), Op2( 2), emulate_tlb_invalidate},
+ /* DTLBIALL */
+ { Op1( 0), CRn( 8), CRm( 6), Op2( 0), emulate_tlb_invalidate},
+ /* DTLBIMVA */
+ { Op1( 0), CRn( 8), CRm( 6), Op2( 1), emulate_tlb_invalidate},
+ /* DTLBIASID */
+ { Op1( 0), CRn( 8), CRm( 6), Op2( 2), emulate_tlb_invalidate},
+ /* TLBIALL */
+ { Op1( 0), CRn( 8), CRm( 7), Op2( 0), emulate_tlb_invalidate},
+ /* TLBIMVA */
+ { Op1( 0), CRn( 8), CRm( 7), Op2( 1), emulate_tlb_invalidate},
+ /* TLBIASID */
+ { Op1( 0), CRn( 8), CRm( 7), Op2( 2), emulate_tlb_invalidate},
+ /* TLBIMVAA */
+ { Op1( 0), CRn( 8), CRm( 7), Op2( 3), emulate_tlb_invalidate},
+ /* TLBIMVAL */
+ { Op1( 0), CRn( 8), CRm( 7), Op2( 5), emulate_tlb_invalidate},
+ /* TLBIMVAAL */
+ { Op1( 0), CRn( 8), CRm( 7), Op2( 7), emulate_tlb_invalidate},
+
/* PMU */
{ Op1( 0), CRn( 9), CRm(12), Op2( 0), access_pmcr },
{ Op1( 0), CRn( 9), CRm(12), Op2( 1), access_pmcnten },
diff --git a/arch/arm64/kvm/trace.h b/arch/arm64/kvm/trace.h
index 7fb0008..c4d577f 100644
--- a/arch/arm64/kvm/trace.h
+++ b/arch/arm64/kvm/trace.h
@@ -166,6 +166,22 @@ TRACE_EVENT(kvm_set_guest_debug,
TP_printk("vcpu: %p, flags: 0x%08x", __entry->vcpu, __entry->guest_debug)
);
+TRACE_EVENT(kvm_tlb_invalidate,
+ TP_PROTO(unsigned long vcpu_pc, u32 opcode),
+ TP_ARGS(vcpu_pc, opcode),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, vcpu_pc)
+ __field(u32, opcode)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_pc = vcpu_pc;
+ __entry->opcode = opcode;
+ ),
+
+ TP_printk("vcpu_pc=0x%16lx opcode=%08x", __entry->vcpu_pc, __entry->opcode)
+);
#endif /* _TRACE_ARM64_KVM_H */
--
2.8.1
[toc] | [next] | [standalone]
| From | Christoffer Dall <christoffer.dall@linaro.org> |
|---|---|
| Date | 2016-09-06 12:20 +0200 |
| Message-ID | <selDX-HZ-1@gated-at.bofh.it> |
| In reply to | #1476591 |
On Mon, Sep 05, 2016 at 05:31:36PM +0100, Punit Agrawal wrote:
> The ARMv8 architecture allows trapping of TLB maintenane instructions
> from EL0/EL1 to higher exception levels. On encountering a trappable TLB
> instruction in a guest, an exception is taken to EL2.
>
> Add functionality to handle emulating the TLB instructions.
>
> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> Cc: Christoffer Dall <christoffer.dall@linaro.org>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> ---
> arch/arm64/include/asm/kvm_asm.h | 1 +
> arch/arm64/kvm/hyp/tlb.c | 69 ++++++++++++++++++++++++++++++++++
> arch/arm64/kvm/sys_regs.c | 81 ++++++++++++++++++++++++++++++++++++++++
> arch/arm64/kvm/trace.h | 16 ++++++++
> 4 files changed, 167 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
> index 7561f63..1ac1cc3 100644
> --- a/arch/arm64/include/asm/kvm_asm.h
> +++ b/arch/arm64/include/asm/kvm_asm.h
> @@ -49,6 +49,7 @@ extern char __kvm_hyp_vector[];
> extern void __kvm_flush_vm_context(void);
> extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
> extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
> +extern void __kvm_emulate_tlb_invalidate(struct kvm *kvm, u32 sysreg, u64 regval);
>
> extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
>
> diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c
> index 4cda100..1210f58 100644
> --- a/arch/arm64/kvm/hyp/tlb.c
> +++ b/arch/arm64/kvm/hyp/tlb.c
> @@ -78,3 +78,72 @@ static void __hyp_text __tlb_flush_vm_context(void)
> }
>
> __alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
> +
> +/* Intentionally empty functions */
> +static void __hyp_text __switch_to_hyp_role_nvhe(void) { }
> +static void __hyp_text __switch_to_host_role_nvhe(void) { }
> +
> +static void __hyp_text __switch_to_hyp_role_vhe(void)
> +{
> + u64 hcr = read_sysreg(hcr_el2);
> +
> + hcr &= ~HCR_TGE;
> + write_sysreg(hcr, hcr_el2);
why do we need to clear TGE for the TLB maintenance instructions to
work?
Perhaps this is worth explaining in a comment.
Otherwise this looks ok to me.
-Christoffer
> +}
> +
> +static void __hyp_text __switch_to_host_role_vhe(void)
> +{
> + u64 hcr = read_sysreg(hcr_el2);
> +
> + hcr |= HCR_TGE;
> + write_sysreg(hcr, hcr_el2);
> +}
> +
> +static hyp_alternate_select(__switch_to_hyp_role,
> + __switch_to_hyp_role_nvhe,
> + __switch_to_hyp_role_vhe,
> + ARM64_HAS_VIRT_HOST_EXTN);
> +
> +static hyp_alternate_select(__switch_to_host_role,
> + __switch_to_host_role_nvhe,
> + __switch_to_host_role_vhe,
> + ARM64_HAS_VIRT_HOST_EXTN);
> +
> +static void __hyp_text __switch_to_guest_regime(struct kvm *kvm)
> +{
> + write_sysreg(kvm->arch.vttbr, vttbr_el2);
> + __switch_to_hyp_role();
> + isb();
> +}
> +
> +static void __hyp_text __switch_to_host_regime(void)
> +{
> + __switch_to_host_role();
> + write_sysreg(0, vttbr_el2);
> +}
> +
> +void __hyp_text
> +__kvm_emulate_tlb_invalidate(struct kvm *kvm, u32 sys_op, u64 regval)
> +{
> + kvm = kern_hyp_va(kvm);
> +
> + /*
> + * Switch to the guest before performing any TLB operations to
> + * target the appropriate VMID
> + */
> + __switch_to_guest_regime(kvm);
> +
> + /*
> + * TLB maintenance operations are broadcast to
> + * inner-shareable domain when HCR_FB is set (default for
> + * KVM).
> + *
> + * Nuke all Stage 1 TLB entries for the VM. This will kill
> + * performance but it's always safe to do as we don't leave
> + * behind any strays in the TLB
> + */
> + __tlbi(vmalle1is);
> + isb();
> +
> + __switch_to_host_regime();
> +}
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index e51367d..0e70da9 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -790,6 +790,18 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
> return true;
> }
>
> +static bool emulate_tlb_invalidate(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
> + const struct sys_reg_desc *r)
> +{
> + u32 opcode = sys_reg(p->Op0, p->Op1, p->CRn, p->CRm, p->Op2);
> +
> + kvm_call_hyp(__kvm_emulate_tlb_invalidate,
> + vcpu->kvm, opcode, p->regval);
> + trace_kvm_tlb_invalidate(*vcpu_pc(vcpu), opcode);
> +
> + return true;
> +}
> +
> /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
> #define DBG_BCR_BVR_WCR_WVR_EL1(n) \
> /* DBGBVRn_EL1 */ \
> @@ -841,6 +853,35 @@ static const struct sys_reg_desc sys_reg_descs[] = {
> { Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010),
> access_dcsw },
>
> + /*
> + * ARMv8 ARM: Table C5-4 TLB maintenance instructions
> + * (Ref: ARMv8 ARM C5.1 version: ARM DDI 0487A.j)
> + */
> + /* TLBI VMALLE1IS */
> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(0), emulate_tlb_invalidate },
> + /* TLBI VAE1IS */
> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(1), emulate_tlb_invalidate },
> + /* TLBI ASIDE1IS */
> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(2), emulate_tlb_invalidate },
> + /* TLBI VAAE1IS */
> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(3), emulate_tlb_invalidate },
> + /* TLBI VALE1IS */
> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(5), emulate_tlb_invalidate },
> + /* TLBI VAALE1IS */
> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(7), emulate_tlb_invalidate },
> + /* TLBI VMALLE1 */
> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(0), emulate_tlb_invalidate },
> + /* TLBI VAE1 */
> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(1), emulate_tlb_invalidate },
> + /* TLBI ASIDE1 */
> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(2), emulate_tlb_invalidate },
> + /* TLBI VAAE1 */
> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(3), emulate_tlb_invalidate },
> + /* TLBI VALE1 */
> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(5), emulate_tlb_invalidate },
> + /* TLBI VAALE1 */
> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(7), emulate_tlb_invalidate },
> +
> DBG_BCR_BVR_WCR_WVR_EL1(0),
> DBG_BCR_BVR_WCR_WVR_EL1(1),
> /* MDCCINT_EL1 */
> @@ -1329,6 +1370,46 @@ static const struct sys_reg_desc cp15_regs[] = {
> { Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
> { Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
>
> + /*
> + * TLB operations
> + */
> + /* TLBIALLIS */
> + { Op1( 0), CRn( 8), CRm( 3), Op2( 0), emulate_tlb_invalidate},
> + /* TLBIMVAIS */
> + { Op1( 0), CRn( 8), CRm( 3), Op2( 1), emulate_tlb_invalidate},
> + /* TLBIASIDIS */
> + { Op1( 0), CRn( 8), CRm( 3), Op2( 2), emulate_tlb_invalidate},
> + /* TLBIMVAAIS */
> + { Op1( 0), CRn( 8), CRm( 3), Op2( 3), emulate_tlb_invalidate},
> + /* TLBIMVALIS */
> + { Op1( 0), CRn( 8), CRm( 3), Op2( 5), emulate_tlb_invalidate},
> + /* TLBIMVAALIS */
> + { Op1( 0), CRn( 8), CRm( 3), Op2( 7), emulate_tlb_invalidate},
> + /* ITLBIALL */
> + { Op1( 0), CRn( 8), CRm( 5), Op2( 0), emulate_tlb_invalidate},
> + /* ITLBIMVA */
> + { Op1( 0), CRn( 8), CRm( 5), Op2( 1), emulate_tlb_invalidate},
> + /* ITLBIASID */
> + { Op1( 0), CRn( 8), CRm( 5), Op2( 2), emulate_tlb_invalidate},
> + /* DTLBIALL */
> + { Op1( 0), CRn( 8), CRm( 6), Op2( 0), emulate_tlb_invalidate},
> + /* DTLBIMVA */
> + { Op1( 0), CRn( 8), CRm( 6), Op2( 1), emulate_tlb_invalidate},
> + /* DTLBIASID */
> + { Op1( 0), CRn( 8), CRm( 6), Op2( 2), emulate_tlb_invalidate},
> + /* TLBIALL */
> + { Op1( 0), CRn( 8), CRm( 7), Op2( 0), emulate_tlb_invalidate},
> + /* TLBIMVA */
> + { Op1( 0), CRn( 8), CRm( 7), Op2( 1), emulate_tlb_invalidate},
> + /* TLBIASID */
> + { Op1( 0), CRn( 8), CRm( 7), Op2( 2), emulate_tlb_invalidate},
> + /* TLBIMVAA */
> + { Op1( 0), CRn( 8), CRm( 7), Op2( 3), emulate_tlb_invalidate},
> + /* TLBIMVAL */
> + { Op1( 0), CRn( 8), CRm( 7), Op2( 5), emulate_tlb_invalidate},
> + /* TLBIMVAAL */
> + { Op1( 0), CRn( 8), CRm( 7), Op2( 7), emulate_tlb_invalidate},
> +
> /* PMU */
> { Op1( 0), CRn( 9), CRm(12), Op2( 0), access_pmcr },
> { Op1( 0), CRn( 9), CRm(12), Op2( 1), access_pmcnten },
> diff --git a/arch/arm64/kvm/trace.h b/arch/arm64/kvm/trace.h
> index 7fb0008..c4d577f 100644
> --- a/arch/arm64/kvm/trace.h
> +++ b/arch/arm64/kvm/trace.h
> @@ -166,6 +166,22 @@ TRACE_EVENT(kvm_set_guest_debug,
> TP_printk("vcpu: %p, flags: 0x%08x", __entry->vcpu, __entry->guest_debug)
> );
>
> +TRACE_EVENT(kvm_tlb_invalidate,
> + TP_PROTO(unsigned long vcpu_pc, u32 opcode),
> + TP_ARGS(vcpu_pc, opcode),
> +
> + TP_STRUCT__entry(
> + __field(unsigned long, vcpu_pc)
> + __field(u32, opcode)
> + ),
> +
> + TP_fast_assign(
> + __entry->vcpu_pc = vcpu_pc;
> + __entry->opcode = opcode;
> + ),
> +
> + TP_printk("vcpu_pc=0x%16lx opcode=%08x", __entry->vcpu_pc, __entry->opcode)
> +);
>
> #endif /* _TRACE_ARM64_KVM_H */
>
> --
> 2.8.1
>
[toc] | [prev] | [next] | [standalone]
| From | Punit Agrawal <punit.agrawal@arm.com> |
|---|---|
| Date | 2016-09-06 17:50 +0200 |
| Message-ID | <seqNj-3PR-1@gated-at.bofh.it> |
| In reply to | #1477241 |
Christoffer Dall <christoffer.dall@linaro.org> writes:
> On Mon, Sep 05, 2016 at 05:31:36PM +0100, Punit Agrawal wrote:
>> The ARMv8 architecture allows trapping of TLB maintenane instructions
>> from EL0/EL1 to higher exception levels. On encountering a trappable TLB
>> instruction in a guest, an exception is taken to EL2.
>>
>> Add functionality to handle emulating the TLB instructions.
>>
>> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
>> Cc: Christoffer Dall <christoffer.dall@linaro.org>
>> Cc: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>> arch/arm64/include/asm/kvm_asm.h | 1 +
>> arch/arm64/kvm/hyp/tlb.c | 69 ++++++++++++++++++++++++++++++++++
>> arch/arm64/kvm/sys_regs.c | 81 ++++++++++++++++++++++++++++++++++++++++
>> arch/arm64/kvm/trace.h | 16 ++++++++
>> 4 files changed, 167 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
>> index 7561f63..1ac1cc3 100644
>> --- a/arch/arm64/include/asm/kvm_asm.h
>> +++ b/arch/arm64/include/asm/kvm_asm.h
>> @@ -49,6 +49,7 @@ extern char __kvm_hyp_vector[];
>> extern void __kvm_flush_vm_context(void);
>> extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
>> extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
>> +extern void __kvm_emulate_tlb_invalidate(struct kvm *kvm, u32 sysreg, u64 regval);
>>
>> extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
>>
>> diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c
>> index 4cda100..1210f58 100644
>> --- a/arch/arm64/kvm/hyp/tlb.c
>> +++ b/arch/arm64/kvm/hyp/tlb.c
>> @@ -78,3 +78,72 @@ static void __hyp_text __tlb_flush_vm_context(void)
>> }
>>
>> __alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
>> +
>> +/* Intentionally empty functions */
>> +static void __hyp_text __switch_to_hyp_role_nvhe(void) { }
>> +static void __hyp_text __switch_to_host_role_nvhe(void) { }
>> +
>> +static void __hyp_text __switch_to_hyp_role_vhe(void)
>> +{
>> + u64 hcr = read_sysreg(hcr_el2);
>> +
>> + hcr &= ~HCR_TGE;
>> + write_sysreg(hcr, hcr_el2);
>
> why do we need to clear TGE for the TLB maintenance instructions to
> work?
>
> Perhaps this is worth explaining in a comment.
I've added the following comment before clearing TGE bit.
"When VHE is enabled and HCR_EL2.TGE=1, EL1&0 TLB operations
apply to EL2&0 translation regime. As we prepare to emulate
guest TLB operation clear HCR_TGE to target TLB operations
to EL1&0 (guest)."
>
> Otherwise this looks ok to me.
Thanks for taking a look.
Punit
>
> -Christoffer
>
>> +}
>> +
>> +static void __hyp_text __switch_to_host_role_vhe(void)
>> +{
>> + u64 hcr = read_sysreg(hcr_el2);
>> +
>> + hcr |= HCR_TGE;
>> + write_sysreg(hcr, hcr_el2);
>> +}
>> +
>> +static hyp_alternate_select(__switch_to_hyp_role,
>> + __switch_to_hyp_role_nvhe,
>> + __switch_to_hyp_role_vhe,
>> + ARM64_HAS_VIRT_HOST_EXTN);
>> +
>> +static hyp_alternate_select(__switch_to_host_role,
>> + __switch_to_host_role_nvhe,
>> + __switch_to_host_role_vhe,
>> + ARM64_HAS_VIRT_HOST_EXTN);
>> +
>> +static void __hyp_text __switch_to_guest_regime(struct kvm *kvm)
>> +{
>> + write_sysreg(kvm->arch.vttbr, vttbr_el2);
>> + __switch_to_hyp_role();
>> + isb();
>> +}
>> +
>> +static void __hyp_text __switch_to_host_regime(void)
>> +{
>> + __switch_to_host_role();
>> + write_sysreg(0, vttbr_el2);
>> +}
>> +
>> +void __hyp_text
>> +__kvm_emulate_tlb_invalidate(struct kvm *kvm, u32 sys_op, u64 regval)
>> +{
>> + kvm = kern_hyp_va(kvm);
>> +
>> + /*
>> + * Switch to the guest before performing any TLB operations to
>> + * target the appropriate VMID
>> + */
>> + __switch_to_guest_regime(kvm);
>> +
>> + /*
>> + * TLB maintenance operations are broadcast to
>> + * inner-shareable domain when HCR_FB is set (default for
>> + * KVM).
>> + *
>> + * Nuke all Stage 1 TLB entries for the VM. This will kill
>> + * performance but it's always safe to do as we don't leave
>> + * behind any strays in the TLB
>> + */
>> + __tlbi(vmalle1is);
>> + isb();
>> +
>> + __switch_to_host_regime();
>> +}
>> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
>> index e51367d..0e70da9 100644
>> --- a/arch/arm64/kvm/sys_regs.c
>> +++ b/arch/arm64/kvm/sys_regs.c
>> @@ -790,6 +790,18 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
>> return true;
>> }
>>
>> +static bool emulate_tlb_invalidate(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
>> + const struct sys_reg_desc *r)
>> +{
>> + u32 opcode = sys_reg(p->Op0, p->Op1, p->CRn, p->CRm, p->Op2);
>> +
>> + kvm_call_hyp(__kvm_emulate_tlb_invalidate,
>> + vcpu->kvm, opcode, p->regval);
>> + trace_kvm_tlb_invalidate(*vcpu_pc(vcpu), opcode);
>> +
>> + return true;
>> +}
>> +
>> /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
>> #define DBG_BCR_BVR_WCR_WVR_EL1(n) \
>> /* DBGBVRn_EL1 */ \
>> @@ -841,6 +853,35 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>> { Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010),
>> access_dcsw },
>>
>> + /*
>> + * ARMv8 ARM: Table C5-4 TLB maintenance instructions
>> + * (Ref: ARMv8 ARM C5.1 version: ARM DDI 0487A.j)
>> + */
>> + /* TLBI VMALLE1IS */
>> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(0), emulate_tlb_invalidate },
>> + /* TLBI VAE1IS */
>> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(1), emulate_tlb_invalidate },
>> + /* TLBI ASIDE1IS */
>> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(2), emulate_tlb_invalidate },
>> + /* TLBI VAAE1IS */
>> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(3), emulate_tlb_invalidate },
>> + /* TLBI VALE1IS */
>> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(5), emulate_tlb_invalidate },
>> + /* TLBI VAALE1IS */
>> + { Op0(1), Op1(0), CRn(8), CRm(3), Op2(7), emulate_tlb_invalidate },
>> + /* TLBI VMALLE1 */
>> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(0), emulate_tlb_invalidate },
>> + /* TLBI VAE1 */
>> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(1), emulate_tlb_invalidate },
>> + /* TLBI ASIDE1 */
>> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(2), emulate_tlb_invalidate },
>> + /* TLBI VAAE1 */
>> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(3), emulate_tlb_invalidate },
>> + /* TLBI VALE1 */
>> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(5), emulate_tlb_invalidate },
>> + /* TLBI VAALE1 */
>> + { Op0(1), Op1(0), CRn(8), CRm(7), Op2(7), emulate_tlb_invalidate },
>> +
>> DBG_BCR_BVR_WCR_WVR_EL1(0),
>> DBG_BCR_BVR_WCR_WVR_EL1(1),
>> /* MDCCINT_EL1 */
>> @@ -1329,6 +1370,46 @@ static const struct sys_reg_desc cp15_regs[] = {
>> { Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
>> { Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
>>
>> + /*
>> + * TLB operations
>> + */
>> + /* TLBIALLIS */
>> + { Op1( 0), CRn( 8), CRm( 3), Op2( 0), emulate_tlb_invalidate},
>> + /* TLBIMVAIS */
>> + { Op1( 0), CRn( 8), CRm( 3), Op2( 1), emulate_tlb_invalidate},
>> + /* TLBIASIDIS */
>> + { Op1( 0), CRn( 8), CRm( 3), Op2( 2), emulate_tlb_invalidate},
>> + /* TLBIMVAAIS */
>> + { Op1( 0), CRn( 8), CRm( 3), Op2( 3), emulate_tlb_invalidate},
>> + /* TLBIMVALIS */
>> + { Op1( 0), CRn( 8), CRm( 3), Op2( 5), emulate_tlb_invalidate},
>> + /* TLBIMVAALIS */
>> + { Op1( 0), CRn( 8), CRm( 3), Op2( 7), emulate_tlb_invalidate},
>> + /* ITLBIALL */
>> + { Op1( 0), CRn( 8), CRm( 5), Op2( 0), emulate_tlb_invalidate},
>> + /* ITLBIMVA */
>> + { Op1( 0), CRn( 8), CRm( 5), Op2( 1), emulate_tlb_invalidate},
>> + /* ITLBIASID */
>> + { Op1( 0), CRn( 8), CRm( 5), Op2( 2), emulate_tlb_invalidate},
>> + /* DTLBIALL */
>> + { Op1( 0), CRn( 8), CRm( 6), Op2( 0), emulate_tlb_invalidate},
>> + /* DTLBIMVA */
>> + { Op1( 0), CRn( 8), CRm( 6), Op2( 1), emulate_tlb_invalidate},
>> + /* DTLBIASID */
>> + { Op1( 0), CRn( 8), CRm( 6), Op2( 2), emulate_tlb_invalidate},
>> + /* TLBIALL */
>> + { Op1( 0), CRn( 8), CRm( 7), Op2( 0), emulate_tlb_invalidate},
>> + /* TLBIMVA */
>> + { Op1( 0), CRn( 8), CRm( 7), Op2( 1), emulate_tlb_invalidate},
>> + /* TLBIASID */
>> + { Op1( 0), CRn( 8), CRm( 7), Op2( 2), emulate_tlb_invalidate},
>> + /* TLBIMVAA */
>> + { Op1( 0), CRn( 8), CRm( 7), Op2( 3), emulate_tlb_invalidate},
>> + /* TLBIMVAL */
>> + { Op1( 0), CRn( 8), CRm( 7), Op2( 5), emulate_tlb_invalidate},
>> + /* TLBIMVAAL */
>> + { Op1( 0), CRn( 8), CRm( 7), Op2( 7), emulate_tlb_invalidate},
>> +
>> /* PMU */
>> { Op1( 0), CRn( 9), CRm(12), Op2( 0), access_pmcr },
>> { Op1( 0), CRn( 9), CRm(12), Op2( 1), access_pmcnten },
>> diff --git a/arch/arm64/kvm/trace.h b/arch/arm64/kvm/trace.h
>> index 7fb0008..c4d577f 100644
>> --- a/arch/arm64/kvm/trace.h
>> +++ b/arch/arm64/kvm/trace.h
>> @@ -166,6 +166,22 @@ TRACE_EVENT(kvm_set_guest_debug,
>> TP_printk("vcpu: %p, flags: 0x%08x", __entry->vcpu, __entry->guest_debug)
>> );
>>
>> +TRACE_EVENT(kvm_tlb_invalidate,
>> + TP_PROTO(unsigned long vcpu_pc, u32 opcode),
>> + TP_ARGS(vcpu_pc, opcode),
>> +
>> + TP_STRUCT__entry(
>> + __field(unsigned long, vcpu_pc)
>> + __field(u32, opcode)
>> + ),
>> +
>> + TP_fast_assign(
>> + __entry->vcpu_pc = vcpu_pc;
>> + __entry->opcode = opcode;
>> + ),
>> +
>> + TP_printk("vcpu_pc=0x%16lx opcode=%08x", __entry->vcpu_pc, __entry->opcode)
>> +);
>>
>> #endif /* _TRACE_ARM64_KVM_H */
>>
>> --
>> 2.8.1
>>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
[toc] | [prev] | [next] | [standalone]
| From | Christoffer Dall <christoffer.dall@linaro.org> |
|---|---|
| Date | 2016-09-06 19:00 +0200 |
| Message-ID | <serT3-4wT-13@gated-at.bofh.it> |
| In reply to | #1477559 |
On Tue, Sep 06, 2016 at 04:44:11PM +0100, Punit Agrawal wrote:
> Christoffer Dall <christoffer.dall@linaro.org> writes:
>
> > On Mon, Sep 05, 2016 at 05:31:36PM +0100, Punit Agrawal wrote:
> >> The ARMv8 architecture allows trapping of TLB maintenane instructions
> >> from EL0/EL1 to higher exception levels. On encountering a trappable TLB
> >> instruction in a guest, an exception is taken to EL2.
> >>
> >> Add functionality to handle emulating the TLB instructions.
> >>
> >> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> >> Cc: Christoffer Dall <christoffer.dall@linaro.org>
> >> Cc: Marc Zyngier <marc.zyngier@arm.com>
> >> ---
> >> arch/arm64/include/asm/kvm_asm.h | 1 +
> >> arch/arm64/kvm/hyp/tlb.c | 69 ++++++++++++++++++++++++++++++++++
> >> arch/arm64/kvm/sys_regs.c | 81 ++++++++++++++++++++++++++++++++++++++++
> >> arch/arm64/kvm/trace.h | 16 ++++++++
> >> 4 files changed, 167 insertions(+)
> >>
> >> diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
> >> index 7561f63..1ac1cc3 100644
> >> --- a/arch/arm64/include/asm/kvm_asm.h
> >> +++ b/arch/arm64/include/asm/kvm_asm.h
> >> @@ -49,6 +49,7 @@ extern char __kvm_hyp_vector[];
> >> extern void __kvm_flush_vm_context(void);
> >> extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
> >> extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
> >> +extern void __kvm_emulate_tlb_invalidate(struct kvm *kvm, u32 sysreg, u64 regval);
> >>
> >> extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
> >>
> >> diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c
> >> index 4cda100..1210f58 100644
> >> --- a/arch/arm64/kvm/hyp/tlb.c
> >> +++ b/arch/arm64/kvm/hyp/tlb.c
> >> @@ -78,3 +78,72 @@ static void __hyp_text __tlb_flush_vm_context(void)
> >> }
> >>
> >> __alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
> >> +
> >> +/* Intentionally empty functions */
> >> +static void __hyp_text __switch_to_hyp_role_nvhe(void) { }
> >> +static void __hyp_text __switch_to_host_role_nvhe(void) { }
> >> +
> >> +static void __hyp_text __switch_to_hyp_role_vhe(void)
> >> +{
> >> + u64 hcr = read_sysreg(hcr_el2);
> >> +
> >> + hcr &= ~HCR_TGE;
> >> + write_sysreg(hcr, hcr_el2);
> >
> > why do we need to clear TGE for the TLB maintenance instructions to
> > work?
> >
> > Perhaps this is worth explaining in a comment.
>
> I've added the following comment before clearing TGE bit.
>
> "When VHE is enabled and HCR_EL2.TGE=1, EL1&0 TLB operations
> apply to EL2&0 translation regime. As we prepare to emulate
> guest TLB operation clear HCR_TGE to target TLB operations
> to EL1&0 (guest)."
>
Ah, right, obvious when I read this comment.
Thanks,
-Christoffer
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web