All of lore.kernel.org
 help / color / mirror / Atom feed
From: cdall@cs.columbia.edu (Christoffer Dall)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 17/32] arm64: KVM: HYP mode world switch implementation
Date: Tue, 23 Apr 2013 15:59:58 -0700	[thread overview]
Message-ID: <20130423225958.GF20569@gmail.com> (raw)
In-Reply-To: <1365437854-30214-18-git-send-email-marc.zyngier@arm.com>

On Mon, Apr 08, 2013 at 05:17:19PM +0100, Marc Zyngier wrote:
> The HYP mode world switch in all its glory.
> 
> Implements save/restore of host/guest registers, EL2 trapping,
> IPA resolution, and additional services (tlb invalidation).
> 
> Reviewed-by: Christopher Covington <cov@codeaurora.org>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  arch/arm64/kernel/asm-offsets.c |  34 +++
>  arch/arm64/kvm/hyp.S            | 602 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 636 insertions(+)
>  create mode 100644 arch/arm64/kvm/hyp.S
> 
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index a2a4d81..666e231 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -21,6 +21,7 @@
>  #include <linux/sched.h>
>  #include <linux/mm.h>
>  #include <linux/dma-mapping.h>
> +#include <linux/kvm_host.h>
>  #include <asm/thread_info.h>
>  #include <asm/memory.h>
>  #include <asm/cputable.h>
> @@ -104,5 +105,38 @@ int main(void)
>    BLANK();
>    DEFINE(TZ_MINWEST,		offsetof(struct timezone, tz_minuteswest));
>    DEFINE(TZ_DSTTIME,		offsetof(struct timezone, tz_dsttime));
> +  BLANK();
> +#ifdef CONFIG_KVM_ARM_HOST
> +  DEFINE(VCPU_CONTEXT,		offsetof(struct kvm_vcpu, arch.ctxt));
> +  DEFINE(CPU_GP_REGS,		offsetof(struct kvm_cpu_context, gp_regs));
> +  DEFINE(CPU_USER_PT_REGS,	offsetof(struct kvm_regs, regs));
> +  DEFINE(CPU_FP_REGS,		offsetof(struct kvm_regs, fp_regs));
> +  DEFINE(CPU_SP_EL1,		offsetof(struct kvm_regs, sp_el1));
> +  DEFINE(CPU_ELR_EL1,		offsetof(struct kvm_regs, elr_el1));
> +  DEFINE(CPU_SPSR,		offsetof(struct kvm_regs, spsr));
> +  DEFINE(CPU_SYSREGS,		offsetof(struct kvm_cpu_context, sys_regs));
> +  DEFINE(VCPU_ESR_EL2,		offsetof(struct kvm_vcpu, arch.fault.esr_el2));
> +  DEFINE(VCPU_FAR_EL2,		offsetof(struct kvm_vcpu, arch.fault.far_el2));
> +  DEFINE(VCPU_HPFAR_EL2,	offsetof(struct kvm_vcpu, arch.fault.hpfar_el2));
> +  DEFINE(VCPU_HCR_EL2,		offsetof(struct kvm_vcpu, arch.hcr_el2));
> +  DEFINE(VCPU_IRQ_LINES,	offsetof(struct kvm_vcpu, arch.irq_lines));
> +  DEFINE(VCPU_HOST_CONTEXT,	offsetof(struct kvm_vcpu, arch.host_cpu_context));
> +  DEFINE(VCPU_TIMER_CNTV_CTL,	offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
> +  DEFINE(VCPU_TIMER_CNTV_CVAL,	offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval));
> +  DEFINE(KVM_TIMER_CNTVOFF,	offsetof(struct kvm, arch.timer.cntvoff));
> +  DEFINE(KVM_TIMER_ENABLED,	offsetof(struct kvm, arch.timer.enabled));
> +  DEFINE(VCPU_KVM,		offsetof(struct kvm_vcpu, kvm));
> +  DEFINE(VCPU_VGIC_CPU,		offsetof(struct kvm_vcpu, arch.vgic_cpu));
> +  DEFINE(VGIC_CPU_HCR,		offsetof(struct vgic_cpu, vgic_hcr));
> +  DEFINE(VGIC_CPU_VMCR,		offsetof(struct vgic_cpu, vgic_vmcr));
> +  DEFINE(VGIC_CPU_MISR,		offsetof(struct vgic_cpu, vgic_misr));
> +  DEFINE(VGIC_CPU_EISR,		offsetof(struct vgic_cpu, vgic_eisr));
> +  DEFINE(VGIC_CPU_ELRSR,	offsetof(struct vgic_cpu, vgic_elrsr));
> +  DEFINE(VGIC_CPU_APR,		offsetof(struct vgic_cpu, vgic_apr));
> +  DEFINE(VGIC_CPU_LR,		offsetof(struct vgic_cpu, vgic_lr));
> +  DEFINE(VGIC_CPU_NR_LR,	offsetof(struct vgic_cpu, nr_lr));
> +  DEFINE(KVM_VTTBR,		offsetof(struct kvm, arch.vttbr));
> +  DEFINE(KVM_VGIC_VCTRL,	offsetof(struct kvm, arch.vgic.vctrl_base));
> +#endif
>    return 0;
>  }
> diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S
> new file mode 100644
> index 0000000..c745d20
> --- /dev/null
> +++ b/arch/arm64/kvm/hyp.S
> @@ -0,0 +1,602 @@
> +/*
> + * Copyright (C) 2012,2013 - ARM Ltd
> + * Author: Marc Zyngier <marc.zyngier@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/linkage.h>
> +#include <linux/irqchip/arm-gic.h>
> +
> +#include <asm/assembler.h>
> +#include <asm/memory.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/fpsimdmacros.h>
> +#include <asm/kvm.h>
> +#include <asm/kvm_asm.h>
> +#include <asm/kvm_arm.h>
> +#include <asm/kvm_mmu.h>
> +
> +#define CPU_GP_REG_OFFSET(x)	(CPU_GP_REGS + x)
> +#define CPU_XREG_OFFSET(x)	CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
> +#define CPU_SPSR_OFFSET(x)	CPU_GP_REG_OFFSET(CPU_SPSR + 8*x)
> +#define CPU_SYSREG_OFFSET(x)	(CPU_SYSREGS + 8*x)
> +
> +	.text
> +	.pushsection	.hyp.text, "ax"
> +	.align	PAGE_SHIFT
> +
> +__kvm_hyp_code_start:
> +	.globl __kvm_hyp_code_start
> +
> +.macro save_common_regs
> +	// x2: base address for cpu context
> +	// x3: tmp register

what's with the C99 style comments? Standard for arm64 assembly?

> +
> +	add	x3, x2, #CPU_XREG_OFFSET(19)
> +	stp	x19, x20, [x3]
> +	stp	x21, x22, [x3, #16]
> +	stp	x23, x24, [x3, #32]
> +	stp	x25, x26, [x3, #48]
> +	stp	x27, x28, [x3, #64]
> +	stp	x29, lr, [x3, #80]
> +
> +	mrs	x19, sp_el0
> +	mrs	x20, elr_el2		// EL1 PC
> +	mrs	x21, spsr_el2		// EL1 pstate
> +
> +	stp	x19, x20, [x3, #96]
> +	str	x21, [x3, #112]
> +
> +	mrs	x22, sp_el1
> +	mrs	x23, elr_el1
> +	mrs	x24, spsr_el1
> +
> +	str	x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
> +	str	x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
> +	str	x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
> +.endm
> +
> +.macro restore_common_regs
> +	// x2: base address for cpu context
> +	// x3: tmp register
> +
> +	ldr	x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
> +	ldr	x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
> +	ldr	x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
> +
> +	msr	sp_el1, x22
> +	msr	elr_el1, x23
> +	msr	spsr_el1, x24
> +
> +	add	x3, x2, #CPU_XREG_OFFSET(31)    // SP_EL0
> +	ldp	x19, x20, [x3]
> +	ldr	x21, [x3, #16]
> +
> +	msr	sp_el0, x19
> +	msr	elr_el2, x20 				// EL1 PC
> +	msr	spsr_el2, x21 				// EL1 pstate
> +
> +	add	x3, x2, #CPU_XREG_OFFSET(19)
> +	ldp	x19, x20, [x3]
> +	ldp	x21, x22, [x3, #16]
> +	ldp	x23, x24, [x3, #32]
> +	ldp	x25, x26, [x3, #48]
> +	ldp	x27, x28, [x3, #64]
> +	ldp	x29, lr, [x3, #80]
> +.endm
> +
> +.macro save_host_regs
> +	save_common_regs
> +.endm
> +
> +.macro restore_host_regs
> +	restore_common_regs
> +.endm
> +
> +.macro save_fpsimd
> +	// x2: cpu context address
> +	// x3, x4: tmp regs
> +	add	x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
> +	fpsimd_save x3, 4
> +.endm
> +
> +.macro restore_fpsimd
> +	// x2: cpu context address
> +	// x3, x4: tmp regs
> +	add	x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
> +	fpsimd_restore x3, 4
> +.endm
> +
> +.macro save_guest_regs
> +	// x0 is the vcpu address
> +	// x1 is the return code, do not corrupt!
> +	// x2 is the cpu context
> +	// x3 is a tmp register
> +	// Guest's x0-x3 are on the stack
> +
> +	// Compute base to save registers
> +	add	x3, x2, #CPU_XREG_OFFSET(4)
> +	stp	x4, x5, [x3]
> +	stp	x6, x7, [x3, #16]
> +	stp	x8, x9, [x3, #32]
> +	stp	x10, x11, [x3, #48]
> +	stp	x12, x13, [x3, #64]
> +	stp	x14, x15, [x3, #80]
> +	stp	x16, x17, [x3, #96]
> +	str	x18, [x3, #112]
> +
> +	pop	x6, x7			// x2, x3
> +	pop	x4, x5			// x0, x1
> +
> +	add	x3, x2, #CPU_XREG_OFFSET(0)
> +	stp	x4, x5, [x3]
> +	stp	x6, x7, [x3, #16]
> +
> +	save_common_regs
> +.endm
> +
> +.macro restore_guest_regs
> +	// x0 is the vcpu address.
> +	// x2 is the cpu context
> +	// x3 is a tmp register
> +
> +	// Prepare x0-x3 for later restore
> +	add	x3, x2, #CPU_XREG_OFFSET(0)
> +	ldp	x4, x5, [x3]
> +	ldp	x6, x7, [x3, #16]
> +	push	x4, x5		// Push x0-x3 on the stack
> +	push	x6, x7
> +
> +	// x4-x18
> +	ldp	x4, x5, [x3, #32]
> +	ldp	x6, x7, [x3, #48]
> +	ldp	x8, x9, [x3, #64]
> +	ldp	x10, x11, [x3, #80]
> +	ldp	x12, x13, [x3, #96]
> +	ldp	x14, x15, [x3, #112]
> +	ldp	x16, x17, [x3, #128]
> +	ldr	x18, [x3, #144]
> +
> +	// x19-x29, lr, sp*, elr*, spsr*
> +	restore_common_regs
> +
> +	// Last bits of the 64bit state
> +	pop	x2, x3
> +	pop	x0, x1
> +
> +	// Do not touch any register after this!
> +.endm
> +
> +/*
> + * Macros to perform system register save/restore.
> + *
> + * Ordering here is absolutely critical, and must be kept consistent
> + * in {save,restore}_sysregs, {save,restore}_guest_32bit_state,
> + * and in kvm_asm.h.
> + *
> + * In other words, don't touch any of these unless you know what
> + * you are doing.
> + */
> +.macro save_sysregs
> +	// x2: base address for cpu context
> +	// x3: tmp register
> +
> +	add	x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
> +
> +	mrs	x4,	vmpidr_el2
> +	mrs	x5,	csselr_el1
> +	mrs	x6,	sctlr_el1
> +	mrs	x7,	actlr_el1
> +	mrs	x8,	cpacr_el1
> +	mrs	x9,	ttbr0_el1
> +	mrs	x10,	ttbr1_el1
> +	mrs	x11,	tcr_el1
> +	mrs	x12,	esr_el1
> +	mrs	x13, 	afsr0_el1
> +	mrs	x14,	afsr1_el1
> +	mrs	x15,	far_el1
> +	mrs	x16,	mair_el1
> +	mrs	x17,	vbar_el1
> +	mrs	x18,	contextidr_el1
> +	mrs	x19,	tpidr_el0
> +	mrs	x20,	tpidrro_el0
> +	mrs	x21,	tpidr_el1
> +	mrs	x22, 	amair_el1
> +	mrs	x23, 	cntkctl_el1
> +
> +	stp	x4, x5, [x3]
> +	stp	x6, x7, [x3, #16]
> +	stp	x8, x9, [x3, #32]
> +	stp	x10, x11, [x3, #48]
> +	stp	x12, x13, [x3, #64]
> +	stp	x14, x15, [x3, #80]
> +	stp	x16, x17, [x3, #96]
> +	stp	x18, x19, [x3, #112]
> +	stp	x20, x21, [x3, #128]
> +	stp	x22, x23, [x3, #144]
> +.endm
> +
> +.macro restore_sysregs
> +	// x2: base address for cpu context
> +	// x3: tmp register
> +
> +	add	x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
> +
> +	ldp	x4, x5, [x3]
> +	ldp	x6, x7, [x3, #16]
> +	ldp	x8, x9, [x3, #32]
> +	ldp	x10, x11, [x3, #48]
> +	ldp	x12, x13, [x3, #64]
> +	ldp	x14, x15, [x3, #80]
> +	ldp	x16, x17, [x3, #96]
> +	ldp	x18, x19, [x3, #112]
> +	ldp	x20, x21, [x3, #128]
> +	ldp	x22, x23, [x3, #144]
> +
> +	msr	vmpidr_el2,	x4
> +	msr	csselr_el1,	x5
> +	msr	sctlr_el1,	x6
> +	msr	actlr_el1,	x7
> +	msr	cpacr_el1,	x8
> +	msr	ttbr0_el1,	x9
> +	msr	ttbr1_el1,	x10
> +	msr	tcr_el1,	x11
> +	msr	esr_el1,	x12
> +	msr	afsr0_el1,	x13
> +	msr	afsr1_el1,	x14
> +	msr	far_el1,	x15
> +	msr	mair_el1,	x16
> +	msr	vbar_el1,	x17
> +	msr	contextidr_el1,	x18
> +	msr	tpidr_el0,	x19
> +	msr	tpidrro_el0,	x20
> +	msr	tpidr_el1,	x21
> +	msr	amair_el1,	x22
> +	msr	cntkctl_el1,	x23
> +.endm
> +
> +.macro activate_traps
> +	ldr	x2, [x0, #VCPU_IRQ_LINES]
> +	ldr	x1, [x0, #VCPU_HCR_EL2]
> +	orr	x2, x2, x1
> +	msr	hcr_el2, x2
> +
> +	ldr	x2, =(CPTR_EL2_TTA)
> +	msr	cptr_el2, x2
> +
> +	ldr	x2, =(1 << 15)	// Trap CP15 Cr=15
> +	msr	hstr_el2, x2
> +
> +	mrs	x2, mdcr_el2
> +	and	x2, x2, #MDCR_EL2_HPMN_MASK
> +	orr	x2, x2, #(MDCR_EL2_TPM | MDCR_EL2_TPMCR)
> +	msr	mdcr_el2, x2
> +.endm
> +
> +.macro deactivate_traps
> +	mov	x2, #HCR_RW
> +	msr	hcr_el2, x2
> +	msr	cptr_el2, xzr
> +	msr	hstr_el2, xzr
> +
> +	mrs	x2, mdcr_el2
> +	and	x2, x2, #MDCR_EL2_HPMN_MASK
> +	msr	mdcr_el2, x2
> +.endm
> +
> +.macro activate_vm
> +	ldr	x1, [x0, #VCPU_KVM]
> +	kern_hyp_va	x1
> +	ldr	x2, [x1, #KVM_VTTBR]
> +	msr	vttbr_el2, x2
> +.endm
> +
> +.macro deactivate_vm
> +	msr	vttbr_el2, xzr
> +.endm
> +
> +__save_sysregs:
> +	save_sysregs
> +	ret
> +
> +__restore_sysregs:
> +	restore_sysregs
> +	ret
> +
> +__save_fpsimd:
> +	save_fpsimd
> +	ret
> +
> +__restore_fpsimd:
> +	restore_fpsimd
> +	ret
> +
> +/*
> + * u64 __kvm_vcpu_run(struct kvm_vcpu *vcpu);
> + *
> + * This is the world switch. The first half of the function
> + * deals with entering the guest, and anything from __kvm_vcpu_return
> + * to the end of the function deals with reentering the host.
> + * On the enter path, only x0 (vcpu pointer) must be preserved until
> + * the last moment. On the exit path, x0 (vcpu pointer) and x1 (exception
> + * code) must both be preserved until the epilogue.
> + * In both cases, x2 points to the CPU context we're saving/restoring from/to.
> + */
> +ENTRY(__kvm_vcpu_run)
> +	kern_hyp_va	x0
> +	msr	tpidr_el2, x0	// Save the vcpu register
> +
> +	// Host context
> +	ldr	x2, [x0, #VCPU_HOST_CONTEXT]
> +	kern_hyp_va x2
> +
> +	save_host_regs
> +	bl __save_fpsimd
> +	bl __save_sysregs
> +
> +	activate_traps
> +	activate_vm
> +
> +	// Guest context
> +	add	x2, x0, #VCPU_CONTEXT
> +
> +	bl __restore_sysregs
> +	bl __restore_fpsimd
> +	restore_guest_regs
> +
> +	// That's it, no more messing around.
> +	clrex
> +	eret
> +
> +__kvm_vcpu_return:
> +	// Assume x0 is the vcpu pointer, x1 the return code
> +	// Guest's x0-x3 are on the stack
> +
> +	// Guest context
> +	add	x2, x0, #VCPU_CONTEXT
> +
> +	save_guest_regs
> +	bl __save_fpsimd
> +	bl __save_sysregs
> +
> +	deactivate_traps
> +	deactivate_vm
> +
> +	// Host context
> +	ldr	x2, [x0, #VCPU_HOST_CONTEXT]
> +	kern_hyp_va x2
> +
> +	bl __restore_sysregs
> +	bl __restore_fpsimd
> +	restore_host_regs
> +	mov	x0, x1
> +	clrex
> +	ret
> +END(__kvm_vcpu_run)
> +
> +// void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
> +ENTRY(__kvm_tlb_flush_vmid_ipa)
> +	kern_hyp_va	x0
> +	ldr	x2, [x0, #KVM_VTTBR]
> +	msr	vttbr_el2, x2
> +	isb
> +
> +	/*
> +	 * We could do so much better if we had the VA as well.
> +	 * Instead, we invalidate Stage-2 for this IPA, and the
> +	 * whole of Stage-1. Weep...
> +	 */
> +	tlbi	ipas2e1is, x1
> +	dsb	sy
> +	tlbi	vmalle1is
> +	dsb	sy
> +	isb
> +
> +	msr	vttbr_el2, xzr
> +	isb
> +	ret
> +ENDPROC(__kvm_tlb_flush_vmid_ipa)
> +
> +ENTRY(__kvm_flush_vm_context)
> +	tlbi	alle1is
> +	ic	ialluis
> +	dsb	sy
> +	isb
> +	ret
> +ENDPROC(__kvm_flush_vm_context)
> +
> +__kvm_hyp_panic:
> +	adr	x0, __hyp_panic_str
> +	adr	x1, 1f
> +	ldp	x2, x3, [x1]
> +	sub	x0, x0, x2
> +	add	x0, x0, x3
> +	mrs	x1, spsr_el2
> +	mrs	x2, elr_el2
> +	mrs	x3, esr_el2
> +	mrs	x4, far_el2
> +	mrs	x5, hpfar_el2
> +	mrs	x6, tpidr_el2
> +
> +	mov	lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
> +		      PSR_MODE_EL1h)
> +	msr	spsr_el2, lr
> +	ldr	lr, =panic
> +	msr	elr_el2, lr
> +	eret
> +
> +	.align	3
> +1:	.quad	HYP_PAGE_OFFSET
> +	.quad	PAGE_OFFSET
> +ENDPROC(__kvm_hyp_panic)
> +
> +__hyp_panic_str:
> +	.ascii	"HYP panic:\nPS:%08x PC:%p ESR:%p\nFAR:%p HPFAR:%p VCPU:%p\n\0"
> +
> +	.align	2
> +
> +ENTRY(kvm_call_hyp)
> +	hvc	#0
> +	ret
> +ENDPROC(kvm_call_hyp)
> +
> +.macro invalid_vector	label, target
> +	.align	2
> +\label:
> +	b \target
> +ENDPROC(\label)
> +.endm
> +
> +	/* None of these should ever happen */
> +	invalid_vector	el2t_sync_invalid, __kvm_hyp_panic
> +	invalid_vector	el2t_irq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2t_fiq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2t_error_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_sync_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_irq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_fiq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_error_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_sync_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_irq_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_fiq_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_error_invalid, __kvm_hyp_panic
> +
> +el1_sync:					// Guest trapped into EL2
> +	push	x0, x1
> +	push	x2, x3
> +
> +	mrs	x1, esr_el2
> +	lsr	x2, x1, #ESR_EL2_EC_SHIFT
> +
> +	cmp	x2, #ESR_EL2_EC_HVC64
> +	b.ne	el1_trap
> +
> +	mrs	x3, vttbr_el2			// If vttbr is valid, the 64bit guest
> +	cbnz	x3, el1_trap			// called HVC
> +
> +	/* Here, we're pretty sure the host called HVC. */
> +	pop	x2, x3
> +	pop	x0, x1
> +
> +	push	lr, xzr
> +
> +	/*
> +	 * Compute the function address in EL2, and shuffle the parameters.
> +	 */
> +	kern_hyp_va	x0
> +	mov	lr, x0
> +	mov	x0, x1
> +	mov	x1, x2
> +	mov	x2, x3
> +	blr	lr
> +
> +	pop	lr, xzr
> +	eret
> +
> +el1_trap:
> +	/*
> +	 * x1: ESR
> +	 * x2: ESR_EC
> +	 */
> +	cmp	x2, #ESR_EL2_EC_DABT
> +	mov	x0, #ESR_EL2_EC_IABT
> +	ccmp	x2, x0, #4, ne
> +	b.ne	1f		// Not an abort we care about

why do we get the hpfar_el2 if it's not an abort (or is this for a
special type of abort) ?

> +
> +	/* This is an abort. Check for permission fault */
> +	and	x2, x1, #ESR_EL2_FSC_TYPE
> +	cmp	x2, #FSC_PERM
> +	b.ne	1f		// Not a permission fault
> +
> +	/*
> +	 * Check for Stage-1 page table walk, which is guaranteed
> +	 * to give a valid HPFAR_EL2.
> +	 */
> +	tbnz	x1, #7, 1f	// S1PTW is set
> +
> +	/*
> +	 * Permission fault, HPFAR_EL2 is invalid.
> +	 * Resolve the IPA the hard way using the guest VA.
> +	 * We always perform an EL1 lookup, as we already
> +	 * went through Stage-1.
> +	 */

What does the last sentence mean exactly?

> +	mrs	x3, far_el2
> +	at	s1e1r, x3
> +	isb
> +
> +	/* Read result */
> +	mrs	x3, par_el1
> +	tbnz	x3, #1, 3f		// Bail out if we failed the translation
> +	ubfx	x3, x3, #12, #36	// Extract IPA
> +	lsl	x3, x3, #4		// and present it like HPFAR
> +	b	2f
> +
> +1:	mrs	x3, hpfar_el2
> +
> +2:	mrs	x0, tpidr_el2
> +	mrs	x2, far_el2
> +	str	x1, [x0, #VCPU_ESR_EL2]
> +	str	x2, [x0, #VCPU_FAR_EL2]
> +	str	x3, [x0, #VCPU_HPFAR_EL2]
> +
> +	mov	x1, #ARM_EXCEPTION_TRAP
> +	b	__kvm_vcpu_return
> +
> +	/*
> +	 * Translation failed. Just return to the guest and
> +	 * let it fault again. Another CPU is probably playing
> +	 * behind our back.
> +	 */

This actually makes me wonder if this is a potential DOS attack from
guests (on the 32-bit code as well), or are we sure that an asynchronous
timer interrupt to the host will always creep in between e.g. a tight
loop playing this trick on us?

> +3:	pop	x2, x3
> +	pop	x0, x1
> +
> +	eret
> +
> +el1_irq:
> +	push	x0, x1
> +	push	x2, x3
> +	mrs	x0, tpidr_el2
> +	mov	x1, #ARM_EXCEPTION_IRQ
> +	b	__kvm_vcpu_return
> +
> +	.ltorg
> +
> +	.align 11
> +
> +ENTRY(__kvm_hyp_vector)
> +	ventry	el2t_sync_invalid		// Synchronous EL2t
> +	ventry	el2t_irq_invalid		// IRQ EL2t
> +	ventry	el2t_fiq_invalid		// FIQ EL2t
> +	ventry	el2t_error_invalid		// Error EL2t
> +
> +	ventry	el2h_sync_invalid		// Synchronous EL2h
> +	ventry	el2h_irq_invalid		// IRQ EL2h
> +	ventry	el2h_fiq_invalid		// FIQ EL2h
> +	ventry	el2h_error_invalid		// Error EL2h
> +
> +	ventry	el1_sync			// Synchronous 64-bit EL1
> +	ventry	el1_irq				// IRQ 64-bit EL1
> +	ventry	el1_fiq_invalid			// FIQ 64-bit EL1
> +	ventry	el1_error_invalid		// Error 64-bit EL1
> +
> +	ventry	el1_sync			// Synchronous 32-bit EL1
> +	ventry	el1_irq				// IRQ 32-bit EL1
> +	ventry	el1_fiq_invalid			// FIQ 32-bit EL1
> +	ventry	el1_error_invalid		// Error 32-bit EL1
> +ENDPROC(__kvm_hyp_vector)
> +
> +__kvm_hyp_code_end:
> +	.globl	__kvm_hyp_code_end
> +
> +	.popsection
> -- 
> 1.8.1.4
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

WARNING: multiple messages have this Message-ID (diff)
From: Christoffer Dall <cdall@cs.columbia.edu>
To: Marc Zyngier <marc.zyngier@arm.com>
Cc: linux-arm-kernel@lists.infradead.org,
	kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org,
	catalin.marinas@arm.com, will.deacon@arm.com,
	Christopher Covington <cov@codeaurora.org>
Subject: Re: [PATCH v3 17/32] arm64: KVM: HYP mode world switch implementation
Date: Tue, 23 Apr 2013 15:59:58 -0700	[thread overview]
Message-ID: <20130423225958.GF20569@gmail.com> (raw)
In-Reply-To: <1365437854-30214-18-git-send-email-marc.zyngier@arm.com>

On Mon, Apr 08, 2013 at 05:17:19PM +0100, Marc Zyngier wrote:
> The HYP mode world switch in all its glory.
> 
> Implements save/restore of host/guest registers, EL2 trapping,
> IPA resolution, and additional services (tlb invalidation).
> 
> Reviewed-by: Christopher Covington <cov@codeaurora.org>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  arch/arm64/kernel/asm-offsets.c |  34 +++
>  arch/arm64/kvm/hyp.S            | 602 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 636 insertions(+)
>  create mode 100644 arch/arm64/kvm/hyp.S
> 
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index a2a4d81..666e231 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -21,6 +21,7 @@
>  #include <linux/sched.h>
>  #include <linux/mm.h>
>  #include <linux/dma-mapping.h>
> +#include <linux/kvm_host.h>
>  #include <asm/thread_info.h>
>  #include <asm/memory.h>
>  #include <asm/cputable.h>
> @@ -104,5 +105,38 @@ int main(void)
>    BLANK();
>    DEFINE(TZ_MINWEST,		offsetof(struct timezone, tz_minuteswest));
>    DEFINE(TZ_DSTTIME,		offsetof(struct timezone, tz_dsttime));
> +  BLANK();
> +#ifdef CONFIG_KVM_ARM_HOST
> +  DEFINE(VCPU_CONTEXT,		offsetof(struct kvm_vcpu, arch.ctxt));
> +  DEFINE(CPU_GP_REGS,		offsetof(struct kvm_cpu_context, gp_regs));
> +  DEFINE(CPU_USER_PT_REGS,	offsetof(struct kvm_regs, regs));
> +  DEFINE(CPU_FP_REGS,		offsetof(struct kvm_regs, fp_regs));
> +  DEFINE(CPU_SP_EL1,		offsetof(struct kvm_regs, sp_el1));
> +  DEFINE(CPU_ELR_EL1,		offsetof(struct kvm_regs, elr_el1));
> +  DEFINE(CPU_SPSR,		offsetof(struct kvm_regs, spsr));
> +  DEFINE(CPU_SYSREGS,		offsetof(struct kvm_cpu_context, sys_regs));
> +  DEFINE(VCPU_ESR_EL2,		offsetof(struct kvm_vcpu, arch.fault.esr_el2));
> +  DEFINE(VCPU_FAR_EL2,		offsetof(struct kvm_vcpu, arch.fault.far_el2));
> +  DEFINE(VCPU_HPFAR_EL2,	offsetof(struct kvm_vcpu, arch.fault.hpfar_el2));
> +  DEFINE(VCPU_HCR_EL2,		offsetof(struct kvm_vcpu, arch.hcr_el2));
> +  DEFINE(VCPU_IRQ_LINES,	offsetof(struct kvm_vcpu, arch.irq_lines));
> +  DEFINE(VCPU_HOST_CONTEXT,	offsetof(struct kvm_vcpu, arch.host_cpu_context));
> +  DEFINE(VCPU_TIMER_CNTV_CTL,	offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
> +  DEFINE(VCPU_TIMER_CNTV_CVAL,	offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval));
> +  DEFINE(KVM_TIMER_CNTVOFF,	offsetof(struct kvm, arch.timer.cntvoff));
> +  DEFINE(KVM_TIMER_ENABLED,	offsetof(struct kvm, arch.timer.enabled));
> +  DEFINE(VCPU_KVM,		offsetof(struct kvm_vcpu, kvm));
> +  DEFINE(VCPU_VGIC_CPU,		offsetof(struct kvm_vcpu, arch.vgic_cpu));
> +  DEFINE(VGIC_CPU_HCR,		offsetof(struct vgic_cpu, vgic_hcr));
> +  DEFINE(VGIC_CPU_VMCR,		offsetof(struct vgic_cpu, vgic_vmcr));
> +  DEFINE(VGIC_CPU_MISR,		offsetof(struct vgic_cpu, vgic_misr));
> +  DEFINE(VGIC_CPU_EISR,		offsetof(struct vgic_cpu, vgic_eisr));
> +  DEFINE(VGIC_CPU_ELRSR,	offsetof(struct vgic_cpu, vgic_elrsr));
> +  DEFINE(VGIC_CPU_APR,		offsetof(struct vgic_cpu, vgic_apr));
> +  DEFINE(VGIC_CPU_LR,		offsetof(struct vgic_cpu, vgic_lr));
> +  DEFINE(VGIC_CPU_NR_LR,	offsetof(struct vgic_cpu, nr_lr));
> +  DEFINE(KVM_VTTBR,		offsetof(struct kvm, arch.vttbr));
> +  DEFINE(KVM_VGIC_VCTRL,	offsetof(struct kvm, arch.vgic.vctrl_base));
> +#endif
>    return 0;
>  }
> diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S
> new file mode 100644
> index 0000000..c745d20
> --- /dev/null
> +++ b/arch/arm64/kvm/hyp.S
> @@ -0,0 +1,602 @@
> +/*
> + * Copyright (C) 2012,2013 - ARM Ltd
> + * Author: Marc Zyngier <marc.zyngier@arm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/linkage.h>
> +#include <linux/irqchip/arm-gic.h>
> +
> +#include <asm/assembler.h>
> +#include <asm/memory.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/fpsimdmacros.h>
> +#include <asm/kvm.h>
> +#include <asm/kvm_asm.h>
> +#include <asm/kvm_arm.h>
> +#include <asm/kvm_mmu.h>
> +
> +#define CPU_GP_REG_OFFSET(x)	(CPU_GP_REGS + x)
> +#define CPU_XREG_OFFSET(x)	CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
> +#define CPU_SPSR_OFFSET(x)	CPU_GP_REG_OFFSET(CPU_SPSR + 8*x)
> +#define CPU_SYSREG_OFFSET(x)	(CPU_SYSREGS + 8*x)
> +
> +	.text
> +	.pushsection	.hyp.text, "ax"
> +	.align	PAGE_SHIFT
> +
> +__kvm_hyp_code_start:
> +	.globl __kvm_hyp_code_start
> +
> +.macro save_common_regs
> +	// x2: base address for cpu context
> +	// x3: tmp register

what's with the C99 style comments? Standard for arm64 assembly?

> +
> +	add	x3, x2, #CPU_XREG_OFFSET(19)
> +	stp	x19, x20, [x3]
> +	stp	x21, x22, [x3, #16]
> +	stp	x23, x24, [x3, #32]
> +	stp	x25, x26, [x3, #48]
> +	stp	x27, x28, [x3, #64]
> +	stp	x29, lr, [x3, #80]
> +
> +	mrs	x19, sp_el0
> +	mrs	x20, elr_el2		// EL1 PC
> +	mrs	x21, spsr_el2		// EL1 pstate
> +
> +	stp	x19, x20, [x3, #96]
> +	str	x21, [x3, #112]
> +
> +	mrs	x22, sp_el1
> +	mrs	x23, elr_el1
> +	mrs	x24, spsr_el1
> +
> +	str	x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
> +	str	x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
> +	str	x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
> +.endm
> +
> +.macro restore_common_regs
> +	// x2: base address for cpu context
> +	// x3: tmp register
> +
> +	ldr	x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
> +	ldr	x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
> +	ldr	x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
> +
> +	msr	sp_el1, x22
> +	msr	elr_el1, x23
> +	msr	spsr_el1, x24
> +
> +	add	x3, x2, #CPU_XREG_OFFSET(31)    // SP_EL0
> +	ldp	x19, x20, [x3]
> +	ldr	x21, [x3, #16]
> +
> +	msr	sp_el0, x19
> +	msr	elr_el2, x20 				// EL1 PC
> +	msr	spsr_el2, x21 				// EL1 pstate
> +
> +	add	x3, x2, #CPU_XREG_OFFSET(19)
> +	ldp	x19, x20, [x3]
> +	ldp	x21, x22, [x3, #16]
> +	ldp	x23, x24, [x3, #32]
> +	ldp	x25, x26, [x3, #48]
> +	ldp	x27, x28, [x3, #64]
> +	ldp	x29, lr, [x3, #80]
> +.endm
> +
> +.macro save_host_regs
> +	save_common_regs
> +.endm
> +
> +.macro restore_host_regs
> +	restore_common_regs
> +.endm
> +
> +.macro save_fpsimd
> +	// x2: cpu context address
> +	// x3, x4: tmp regs
> +	add	x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
> +	fpsimd_save x3, 4
> +.endm
> +
> +.macro restore_fpsimd
> +	// x2: cpu context address
> +	// x3, x4: tmp regs
> +	add	x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
> +	fpsimd_restore x3, 4
> +.endm
> +
> +.macro save_guest_regs
> +	// x0 is the vcpu address
> +	// x1 is the return code, do not corrupt!
> +	// x2 is the cpu context
> +	// x3 is a tmp register
> +	// Guest's x0-x3 are on the stack
> +
> +	// Compute base to save registers
> +	add	x3, x2, #CPU_XREG_OFFSET(4)
> +	stp	x4, x5, [x3]
> +	stp	x6, x7, [x3, #16]
> +	stp	x8, x9, [x3, #32]
> +	stp	x10, x11, [x3, #48]
> +	stp	x12, x13, [x3, #64]
> +	stp	x14, x15, [x3, #80]
> +	stp	x16, x17, [x3, #96]
> +	str	x18, [x3, #112]
> +
> +	pop	x6, x7			// x2, x3
> +	pop	x4, x5			// x0, x1
> +
> +	add	x3, x2, #CPU_XREG_OFFSET(0)
> +	stp	x4, x5, [x3]
> +	stp	x6, x7, [x3, #16]
> +
> +	save_common_regs
> +.endm
> +
> +.macro restore_guest_regs
> +	// x0 is the vcpu address.
> +	// x2 is the cpu context
> +	// x3 is a tmp register
> +
> +	// Prepare x0-x3 for later restore
> +	add	x3, x2, #CPU_XREG_OFFSET(0)
> +	ldp	x4, x5, [x3]
> +	ldp	x6, x7, [x3, #16]
> +	push	x4, x5		// Push x0-x3 on the stack
> +	push	x6, x7
> +
> +	// x4-x18
> +	ldp	x4, x5, [x3, #32]
> +	ldp	x6, x7, [x3, #48]
> +	ldp	x8, x9, [x3, #64]
> +	ldp	x10, x11, [x3, #80]
> +	ldp	x12, x13, [x3, #96]
> +	ldp	x14, x15, [x3, #112]
> +	ldp	x16, x17, [x3, #128]
> +	ldr	x18, [x3, #144]
> +
> +	// x19-x29, lr, sp*, elr*, spsr*
> +	restore_common_regs
> +
> +	// Last bits of the 64bit state
> +	pop	x2, x3
> +	pop	x0, x1
> +
> +	// Do not touch any register after this!
> +.endm
> +
> +/*
> + * Macros to perform system register save/restore.
> + *
> + * Ordering here is absolutely critical, and must be kept consistent
> + * in {save,restore}_sysregs, {save,restore}_guest_32bit_state,
> + * and in kvm_asm.h.
> + *
> + * In other words, don't touch any of these unless you know what
> + * you are doing.
> + */
> +.macro save_sysregs
> +	// x2: base address for cpu context
> +	// x3: tmp register
> +
> +	add	x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
> +
> +	mrs	x4,	vmpidr_el2
> +	mrs	x5,	csselr_el1
> +	mrs	x6,	sctlr_el1
> +	mrs	x7,	actlr_el1
> +	mrs	x8,	cpacr_el1
> +	mrs	x9,	ttbr0_el1
> +	mrs	x10,	ttbr1_el1
> +	mrs	x11,	tcr_el1
> +	mrs	x12,	esr_el1
> +	mrs	x13, 	afsr0_el1
> +	mrs	x14,	afsr1_el1
> +	mrs	x15,	far_el1
> +	mrs	x16,	mair_el1
> +	mrs	x17,	vbar_el1
> +	mrs	x18,	contextidr_el1
> +	mrs	x19,	tpidr_el0
> +	mrs	x20,	tpidrro_el0
> +	mrs	x21,	tpidr_el1
> +	mrs	x22, 	amair_el1
> +	mrs	x23, 	cntkctl_el1
> +
> +	stp	x4, x5, [x3]
> +	stp	x6, x7, [x3, #16]
> +	stp	x8, x9, [x3, #32]
> +	stp	x10, x11, [x3, #48]
> +	stp	x12, x13, [x3, #64]
> +	stp	x14, x15, [x3, #80]
> +	stp	x16, x17, [x3, #96]
> +	stp	x18, x19, [x3, #112]
> +	stp	x20, x21, [x3, #128]
> +	stp	x22, x23, [x3, #144]
> +.endm
> +
> +.macro restore_sysregs
> +	// x2: base address for cpu context
> +	// x3: tmp register
> +
> +	add	x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
> +
> +	ldp	x4, x5, [x3]
> +	ldp	x6, x7, [x3, #16]
> +	ldp	x8, x9, [x3, #32]
> +	ldp	x10, x11, [x3, #48]
> +	ldp	x12, x13, [x3, #64]
> +	ldp	x14, x15, [x3, #80]
> +	ldp	x16, x17, [x3, #96]
> +	ldp	x18, x19, [x3, #112]
> +	ldp	x20, x21, [x3, #128]
> +	ldp	x22, x23, [x3, #144]
> +
> +	msr	vmpidr_el2,	x4
> +	msr	csselr_el1,	x5
> +	msr	sctlr_el1,	x6
> +	msr	actlr_el1,	x7
> +	msr	cpacr_el1,	x8
> +	msr	ttbr0_el1,	x9
> +	msr	ttbr1_el1,	x10
> +	msr	tcr_el1,	x11
> +	msr	esr_el1,	x12
> +	msr	afsr0_el1,	x13
> +	msr	afsr1_el1,	x14
> +	msr	far_el1,	x15
> +	msr	mair_el1,	x16
> +	msr	vbar_el1,	x17
> +	msr	contextidr_el1,	x18
> +	msr	tpidr_el0,	x19
> +	msr	tpidrro_el0,	x20
> +	msr	tpidr_el1,	x21
> +	msr	amair_el1,	x22
> +	msr	cntkctl_el1,	x23
> +.endm
> +
> +.macro activate_traps
> +	ldr	x2, [x0, #VCPU_IRQ_LINES]
> +	ldr	x1, [x0, #VCPU_HCR_EL2]
> +	orr	x2, x2, x1
> +	msr	hcr_el2, x2
> +
> +	ldr	x2, =(CPTR_EL2_TTA)
> +	msr	cptr_el2, x2
> +
> +	ldr	x2, =(1 << 15)	// Trap CP15 Cr=15
> +	msr	hstr_el2, x2
> +
> +	mrs	x2, mdcr_el2
> +	and	x2, x2, #MDCR_EL2_HPMN_MASK
> +	orr	x2, x2, #(MDCR_EL2_TPM | MDCR_EL2_TPMCR)
> +	msr	mdcr_el2, x2
> +.endm
> +
> +.macro deactivate_traps
> +	mov	x2, #HCR_RW
> +	msr	hcr_el2, x2
> +	msr	cptr_el2, xzr
> +	msr	hstr_el2, xzr
> +
> +	mrs	x2, mdcr_el2
> +	and	x2, x2, #MDCR_EL2_HPMN_MASK
> +	msr	mdcr_el2, x2
> +.endm
> +
> +.macro activate_vm
> +	ldr	x1, [x0, #VCPU_KVM]
> +	kern_hyp_va	x1
> +	ldr	x2, [x1, #KVM_VTTBR]
> +	msr	vttbr_el2, x2
> +.endm
> +
> +.macro deactivate_vm
> +	msr	vttbr_el2, xzr
> +.endm
> +
> +__save_sysregs:
> +	save_sysregs
> +	ret
> +
> +__restore_sysregs:
> +	restore_sysregs
> +	ret
> +
> +__save_fpsimd:
> +	save_fpsimd
> +	ret
> +
> +__restore_fpsimd:
> +	restore_fpsimd
> +	ret
> +
> +/*
> + * u64 __kvm_vcpu_run(struct kvm_vcpu *vcpu);
> + *
> + * This is the world switch. The first half of the function
> + * deals with entering the guest, and anything from __kvm_vcpu_return
> + * to the end of the function deals with reentering the host.
> + * On the enter path, only x0 (vcpu pointer) must be preserved until
> + * the last moment. On the exit path, x0 (vcpu pointer) and x1 (exception
> + * code) must both be preserved until the epilogue.
> + * In both cases, x2 points to the CPU context we're saving/restoring from/to.
> + */
> +ENTRY(__kvm_vcpu_run)
> +	kern_hyp_va	x0
> +	msr	tpidr_el2, x0	// Save the vcpu register
> +
> +	// Host context
> +	ldr	x2, [x0, #VCPU_HOST_CONTEXT]
> +	kern_hyp_va x2
> +
> +	save_host_regs
> +	bl __save_fpsimd
> +	bl __save_sysregs
> +
> +	activate_traps
> +	activate_vm
> +
> +	// Guest context
> +	add	x2, x0, #VCPU_CONTEXT
> +
> +	bl __restore_sysregs
> +	bl __restore_fpsimd
> +	restore_guest_regs
> +
> +	// That's it, no more messing around.
> +	clrex
> +	eret
> +
> +__kvm_vcpu_return:
> +	// Assume x0 is the vcpu pointer, x1 the return code
> +	// Guest's x0-x3 are on the stack
> +
> +	// Guest context
> +	add	x2, x0, #VCPU_CONTEXT
> +
> +	save_guest_regs
> +	bl __save_fpsimd
> +	bl __save_sysregs
> +
> +	deactivate_traps
> +	deactivate_vm
> +
> +	// Host context
> +	ldr	x2, [x0, #VCPU_HOST_CONTEXT]
> +	kern_hyp_va x2
> +
> +	bl __restore_sysregs
> +	bl __restore_fpsimd
> +	restore_host_regs
> +	mov	x0, x1
> +	clrex
> +	ret
> +END(__kvm_vcpu_run)
> +
> +// void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
> +ENTRY(__kvm_tlb_flush_vmid_ipa)
> +	kern_hyp_va	x0
> +	ldr	x2, [x0, #KVM_VTTBR]
> +	msr	vttbr_el2, x2
> +	isb
> +
> +	/*
> +	 * We could do so much better if we had the VA as well.
> +	 * Instead, we invalidate Stage-2 for this IPA, and the
> +	 * whole of Stage-1. Weep...
> +	 */
> +	tlbi	ipas2e1is, x1
> +	dsb	sy
> +	tlbi	vmalle1is
> +	dsb	sy
> +	isb
> +
> +	msr	vttbr_el2, xzr
> +	isb
> +	ret
> +ENDPROC(__kvm_tlb_flush_vmid_ipa)
> +
> +ENTRY(__kvm_flush_vm_context)
> +	tlbi	alle1is
> +	ic	ialluis
> +	dsb	sy
> +	isb
> +	ret
> +ENDPROC(__kvm_flush_vm_context)
> +
> +__kvm_hyp_panic:
> +	adr	x0, __hyp_panic_str
> +	adr	x1, 1f
> +	ldp	x2, x3, [x1]
> +	sub	x0, x0, x2
> +	add	x0, x0, x3
> +	mrs	x1, spsr_el2
> +	mrs	x2, elr_el2
> +	mrs	x3, esr_el2
> +	mrs	x4, far_el2
> +	mrs	x5, hpfar_el2
> +	mrs	x6, tpidr_el2
> +
> +	mov	lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
> +		      PSR_MODE_EL1h)
> +	msr	spsr_el2, lr
> +	ldr	lr, =panic
> +	msr	elr_el2, lr
> +	eret
> +
> +	.align	3
> +1:	.quad	HYP_PAGE_OFFSET
> +	.quad	PAGE_OFFSET
> +ENDPROC(__kvm_hyp_panic)
> +
> +__hyp_panic_str:
> +	.ascii	"HYP panic:\nPS:%08x PC:%p ESR:%p\nFAR:%p HPFAR:%p VCPU:%p\n\0"
> +
> +	.align	2
> +
> +ENTRY(kvm_call_hyp)
> +	hvc	#0
> +	ret
> +ENDPROC(kvm_call_hyp)
> +
> +.macro invalid_vector	label, target
> +	.align	2
> +\label:
> +	b \target
> +ENDPROC(\label)
> +.endm
> +
> +	/* None of these should ever happen */
> +	invalid_vector	el2t_sync_invalid, __kvm_hyp_panic
> +	invalid_vector	el2t_irq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2t_fiq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2t_error_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_sync_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_irq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_fiq_invalid, __kvm_hyp_panic
> +	invalid_vector	el2h_error_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_sync_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_irq_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_fiq_invalid, __kvm_hyp_panic
> +	invalid_vector	el1_error_invalid, __kvm_hyp_panic
> +
> +el1_sync:					// Guest trapped into EL2
> +	push	x0, x1
> +	push	x2, x3
> +
> +	mrs	x1, esr_el2
> +	lsr	x2, x1, #ESR_EL2_EC_SHIFT
> +
> +	cmp	x2, #ESR_EL2_EC_HVC64
> +	b.ne	el1_trap
> +
> +	mrs	x3, vttbr_el2			// If vttbr is valid, the 64bit guest
> +	cbnz	x3, el1_trap			// called HVC
> +
> +	/* Here, we're pretty sure the host called HVC. */
> +	pop	x2, x3
> +	pop	x0, x1
> +
> +	push	lr, xzr
> +
> +	/*
> +	 * Compute the function address in EL2, and shuffle the parameters.
> +	 */
> +	kern_hyp_va	x0
> +	mov	lr, x0
> +	mov	x0, x1
> +	mov	x1, x2
> +	mov	x2, x3
> +	blr	lr
> +
> +	pop	lr, xzr
> +	eret
> +
> +el1_trap:
> +	/*
> +	 * x1: ESR
> +	 * x2: ESR_EC
> +	 */
> +	cmp	x2, #ESR_EL2_EC_DABT
> +	mov	x0, #ESR_EL2_EC_IABT
> +	ccmp	x2, x0, #4, ne
> +	b.ne	1f		// Not an abort we care about

why do we get the hpfar_el2 if it's not an abort (or is this for a
special type of abort) ?

> +
> +	/* This is an abort. Check for permission fault */
> +	and	x2, x1, #ESR_EL2_FSC_TYPE
> +	cmp	x2, #FSC_PERM
> +	b.ne	1f		// Not a permission fault
> +
> +	/*
> +	 * Check for Stage-1 page table walk, which is guaranteed
> +	 * to give a valid HPFAR_EL2.
> +	 */
> +	tbnz	x1, #7, 1f	// S1PTW is set
> +
> +	/*
> +	 * Permission fault, HPFAR_EL2 is invalid.
> +	 * Resolve the IPA the hard way using the guest VA.
> +	 * We always perform an EL1 lookup, as we already
> +	 * went through Stage-1.
> +	 */

What does the last sentence mean exactly?

> +	mrs	x3, far_el2
> +	at	s1e1r, x3
> +	isb
> +
> +	/* Read result */
> +	mrs	x3, par_el1
> +	tbnz	x3, #1, 3f		// Bail out if we failed the translation
> +	ubfx	x3, x3, #12, #36	// Extract IPA
> +	lsl	x3, x3, #4		// and present it like HPFAR
> +	b	2f
> +
> +1:	mrs	x3, hpfar_el2
> +
> +2:	mrs	x0, tpidr_el2
> +	mrs	x2, far_el2
> +	str	x1, [x0, #VCPU_ESR_EL2]
> +	str	x2, [x0, #VCPU_FAR_EL2]
> +	str	x3, [x0, #VCPU_HPFAR_EL2]
> +
> +	mov	x1, #ARM_EXCEPTION_TRAP
> +	b	__kvm_vcpu_return
> +
> +	/*
> +	 * Translation failed. Just return to the guest and
> +	 * let it fault again. Another CPU is probably playing
> +	 * behind our back.
> +	 */

This actually makes me wonder if this is a potential DOS attack from
guests (on the 32-bit code as well), or are we sure that an asynchronous
timer interrupt to the host will always creep in between e.g. a tight
loop playing this trick on us?

> +3:	pop	x2, x3
> +	pop	x0, x1
> +
> +	eret
> +
> +el1_irq:
> +	push	x0, x1
> +	push	x2, x3
> +	mrs	x0, tpidr_el2
> +	mov	x1, #ARM_EXCEPTION_IRQ
> +	b	__kvm_vcpu_return
> +
> +	.ltorg
> +
> +	.align 11
> +
> +ENTRY(__kvm_hyp_vector)
> +	ventry	el2t_sync_invalid		// Synchronous EL2t
> +	ventry	el2t_irq_invalid		// IRQ EL2t
> +	ventry	el2t_fiq_invalid		// FIQ EL2t
> +	ventry	el2t_error_invalid		// Error EL2t
> +
> +	ventry	el2h_sync_invalid		// Synchronous EL2h
> +	ventry	el2h_irq_invalid		// IRQ EL2h
> +	ventry	el2h_fiq_invalid		// FIQ EL2h
> +	ventry	el2h_error_invalid		// Error EL2h
> +
> +	ventry	el1_sync			// Synchronous 64-bit EL1
> +	ventry	el1_irq				// IRQ 64-bit EL1
> +	ventry	el1_fiq_invalid			// FIQ 64-bit EL1
> +	ventry	el1_error_invalid		// Error 64-bit EL1
> +
> +	ventry	el1_sync			// Synchronous 32-bit EL1
> +	ventry	el1_irq				// IRQ 32-bit EL1
> +	ventry	el1_fiq_invalid			// FIQ 32-bit EL1
> +	ventry	el1_error_invalid		// Error 32-bit EL1
> +ENDPROC(__kvm_hyp_vector)
> +
> +__kvm_hyp_code_end:
> +	.globl	__kvm_hyp_code_end
> +
> +	.popsection
> -- 
> 1.8.1.4
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

  reply	other threads:[~2013-04-23 22:59 UTC|newest]

Thread overview: 232+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-04-08 16:17 [PATCH v3 00/32] Port of KVM to arm64 Marc Zyngier
2013-04-08 16:17 ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 01/32] arm64: add explicit symbols to ESR_EL1 decoding Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 02/32] arm64: KVM: define HYP and Stage-2 translation page flags Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-10 14:07   ` Will Deacon
2013-04-10 14:07     ` Will Deacon
2013-04-12 15:22     ` Marc Zyngier
2013-04-12 15:22       ` Marc Zyngier
2013-04-26 17:01   ` Catalin Marinas
2013-04-26 17:01     ` Catalin Marinas
2013-04-26 17:11     ` Marc Zyngier
2013-04-26 17:11       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 03/32] arm64: KVM: HYP mode idmap support Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 22:57   ` Christoffer Dall
2013-04-23 22:57     ` Christoffer Dall
2013-04-24  9:36     ` Marc Zyngier
2013-04-24  9:36       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 04/32] arm64: KVM: EL2 register definitions Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 05/32] arm64: KVM: system register definitions for 64bit guests Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 06/32] arm64: KVM: Basic ESR_EL2 helpers and vcpu register access Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 07/32] arm64: KVM: fault injection into a guest Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-10 16:40   ` Will Deacon
2013-04-10 16:40     ` Will Deacon
2013-04-12 15:29     ` Marc Zyngier
2013-04-12 15:29       ` Marc Zyngier
2013-04-23 22:57   ` Christoffer Dall
2013-04-23 22:57     ` Christoffer Dall
2013-04-24 10:04     ` Marc Zyngier
2013-04-24 10:04       ` Marc Zyngier
2013-04-24 16:46       ` Christoffer Dall
2013-04-24 16:46         ` Christoffer Dall
2013-04-29 16:26   ` Catalin Marinas
2013-04-29 16:26     ` Catalin Marinas
2013-05-07 16:29     ` Marc Zyngier
2013-05-07 16:29       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 08/32] arm64: KVM: architecture specific MMU backend Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 22:58   ` Christoffer Dall
2013-04-23 22:58     ` Christoffer Dall
2013-04-24 11:03     ` Marc Zyngier
2013-04-24 11:03       ` Marc Zyngier
2013-04-24 11:10       ` Will Deacon
2013-04-24 11:10         ` Will Deacon
2013-04-24 16:50         ` Christoffer Dall
2013-04-24 16:50           ` Christoffer Dall
2013-04-24 16:55       ` Christoffer Dall
2013-04-24 16:55         ` Christoffer Dall
2013-04-25 12:59         ` Marc Zyngier
2013-04-25 12:59           ` Marc Zyngier
2013-04-25 15:13           ` Christoffer Dall
2013-04-25 15:13             ` Christoffer Dall
2013-04-29 17:35   ` Catalin Marinas
2013-04-29 17:35     ` Catalin Marinas
2013-04-30 10:23     ` Marc Zyngier
2013-04-30 10:23       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 09/32] arm64: KVM: user space interface Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-10 16:45   ` Will Deacon
2013-04-10 16:45     ` Will Deacon
2013-04-12 15:31     ` Marc Zyngier
2013-04-12 15:31       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 10/32] arm64: KVM: system register handling Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-10 17:04   ` Will Deacon
2013-04-10 17:04     ` Will Deacon
2013-04-12 15:48     ` Marc Zyngier
2013-04-12 15:48       ` Marc Zyngier
2013-04-23 23:01   ` Christoffer Dall
2013-04-23 23:01     ` Christoffer Dall
2013-04-24 13:37     ` Marc Zyngier
2013-04-24 13:37       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 11/32] arm64: KVM: CPU specific system registers handling Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-10 17:06   ` Will Deacon
2013-04-10 17:06     ` Will Deacon
2013-04-12 16:04     ` Marc Zyngier
2013-04-12 16:04       ` Marc Zyngier
2013-04-23 22:59       ` Christoffer Dall
2013-04-23 22:59         ` Christoffer Dall
2013-04-24  9:33         ` Marc Zyngier
2013-04-24  9:33           ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 12/32] arm64: KVM: virtual CPU reset Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-10 17:07   ` Will Deacon
2013-04-10 17:07     ` Will Deacon
2013-04-12 16:04     ` Marc Zyngier
2013-04-12 16:04       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 13/32] arm64: KVM: kvm_arch and kvm_vcpu_arch definitions Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 14/32] arm64: KVM: MMIO access backend Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 15/32] arm64: KVM: guest one-reg interface Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-10 17:13   ` Will Deacon
2013-04-10 17:13     ` Will Deacon
2013-04-12 16:35     ` Marc Zyngier
2013-04-12 16:35       ` Marc Zyngier
2013-04-23 22:59   ` Christoffer Dall
2013-04-23 22:59     ` Christoffer Dall
2013-04-24 11:27     ` Marc Zyngier
2013-04-24 11:27       ` Marc Zyngier
2013-04-24 17:05       ` Christoffer Dall
2013-04-24 17:05         ` Christoffer Dall
2013-04-08 16:17 ` [PATCH v3 16/32] arm64: KVM: hypervisor initialization code Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-05-02 11:03   ` Catalin Marinas
2013-05-02 11:03     ` Catalin Marinas
2013-05-02 13:28     ` Marc Zyngier
2013-05-02 13:28       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 17/32] arm64: KVM: HYP mode world switch implementation Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 22:59   ` Christoffer Dall [this message]
2013-04-23 22:59     ` Christoffer Dall
2013-04-24 11:39     ` Marc Zyngier
2013-04-24 11:39       ` Marc Zyngier
2013-04-24 17:08       ` Christoffer Dall
2013-04-24 17:08         ` Christoffer Dall
2013-04-08 16:17 ` [PATCH v3 18/32] arm64: KVM: Exit handling Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 19/32] arm64: KVM: Plug the VGIC Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:00   ` Christoffer Dall
2013-04-23 23:00     ` Christoffer Dall
2013-04-24 11:43     ` Marc Zyngier
2013-04-24 11:43       ` Marc Zyngier
2013-05-02 14:38       ` Catalin Marinas
2013-05-02 14:38         ` Catalin Marinas
2013-04-08 16:17 ` [PATCH v3 20/32] arm64: KVM: Plug the arch timer Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:00   ` Christoffer Dall
2013-04-23 23:00     ` Christoffer Dall
2013-04-24 11:43     ` Marc Zyngier
2013-04-24 11:43       ` Marc Zyngier
2013-05-02 15:31       ` Catalin Marinas
2013-05-02 15:31         ` Catalin Marinas
2013-04-08 16:17 ` [PATCH v3 21/32] arm64: KVM: PSCI implementation Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 22/32] arm64: KVM: Build system integration Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 23/32] arm64: KVM: define 32bit specific registers Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 24/32] arm64: KVM: 32bit GP register access Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:00   ` Christoffer Dall
2013-04-23 23:00     ` Christoffer Dall
2013-04-24 13:06     ` Marc Zyngier
2013-04-24 13:06       ` Marc Zyngier
2013-04-24 17:09       ` Christoffer Dall
2013-04-24 17:09         ` Christoffer Dall
2013-05-02 16:09   ` Catalin Marinas
2013-05-02 16:09     ` Catalin Marinas
2013-05-07 16:28     ` Marc Zyngier
2013-05-07 16:28       ` Marc Zyngier
2013-05-07 16:33       ` Catalin Marinas
2013-05-07 16:33         ` Catalin Marinas
2013-05-11  0:36         ` Christoffer Dall
2013-05-11  0:36           ` Christoffer Dall
2013-05-11  7:51           ` Peter Maydell
2013-05-11  7:51             ` Peter Maydell
2013-05-11  9:43           ` Catalin Marinas
2013-05-11  9:43             ` Catalin Marinas
2013-05-12 18:51             ` Christoffer Dall
2013-05-12 18:51               ` Christoffer Dall
2013-04-08 16:17 ` [PATCH v3 25/32] arm64: KVM: 32bit conditional execution emulation Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:00   ` Christoffer Dall
2013-04-23 23:00     ` Christoffer Dall
2013-04-24 13:13     ` Marc Zyngier
2013-04-24 13:13       ` Marc Zyngier
2013-04-24 17:11       ` Christoffer Dall
2013-04-24 17:11         ` Christoffer Dall
2013-04-08 16:17 ` [PATCH v3 26/32] arm64: KVM: 32bit handling of coprocessor traps Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:01   ` Christoffer Dall
2013-04-23 23:01     ` Christoffer Dall
2013-04-24 13:42     ` Marc Zyngier
2013-04-24 13:42       ` Marc Zyngier
2013-04-24 17:14       ` Christoffer Dall
2013-04-24 17:14         ` Christoffer Dall
2013-04-08 16:17 ` [PATCH v3 27/32] arm64: KVM: CPU specific 32bit coprocessor access Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 28/32] arm64: KVM: 32bit specific register world switch Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 29/32] arm64: KVM: 32bit guest fault injection Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:02   ` Christoffer Dall
2013-04-23 23:02     ` Christoffer Dall
2013-04-24 13:46     ` Marc Zyngier
2013-04-24 13:46       ` Marc Zyngier
2013-04-24 17:15       ` Christoffer Dall
2013-04-24 17:15         ` Christoffer Dall
2013-04-08 16:17 ` [PATCH v3 30/32] arm64: KVM: enable initialization of a 32bit vcpu Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:02   ` Christoffer Dall
2013-04-23 23:02     ` Christoffer Dall
2013-04-24 13:49     ` Marc Zyngier
2013-04-24 13:49       ` Marc Zyngier
2013-04-24 17:17       ` Christoffer Dall
2013-04-24 17:17         ` Christoffer Dall
2013-05-07 16:36         ` Marc Zyngier
2013-05-07 16:36           ` Marc Zyngier
2013-05-11  0:38           ` Christoffer Dall
2013-05-11  0:38             ` Christoffer Dall
2013-05-11  8:04             ` Peter Maydell
2013-05-11  8:04               ` Peter Maydell
2013-05-11 16:26               ` Christoffer Dall
2013-05-11 16:26                 ` Christoffer Dall
2013-05-11 16:31                 ` Peter Maydell
2013-05-11 16:31                   ` Peter Maydell
2013-05-13  9:01             ` Marc Zyngier
2013-05-13  9:01               ` Marc Zyngier
2013-05-13 15:46               ` Christoffer Dall
2013-05-13 15:46                 ` Christoffer Dall
2013-04-08 16:17 ` [PATCH v3 31/32] arm64: KVM: userspace API documentation Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:02   ` Christoffer Dall
2013-04-23 23:02     ` Christoffer Dall
2013-04-24 13:52     ` Marc Zyngier
2013-04-24 13:52       ` Marc Zyngier
2013-04-08 16:17 ` [PATCH v3 32/32] arm64: KVM: MAINTAINERS update Marc Zyngier
2013-04-08 16:17   ` Marc Zyngier
2013-04-23 23:04 ` [PATCH v3 00/32] Port of KVM to arm64 Christoffer Dall
2013-04-23 23:04   ` Christoffer Dall
2013-05-03 13:17 ` Catalin Marinas
2013-05-03 13:17   ` Catalin Marinas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20130423225958.GF20569@gmail.com \
    --to=cdall@cs.columbia.edu \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.