162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015-2018 - ARM Ltd
462306a36Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/arm-smccc.h>
862306a36Sopenharmony_ci#include <linux/linkage.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/alternative.h>
1162306a36Sopenharmony_ci#include <asm/assembler.h>
1262306a36Sopenharmony_ci#include <asm/cpufeature.h>
1362306a36Sopenharmony_ci#include <asm/kvm_arm.h>
1462306a36Sopenharmony_ci#include <asm/kvm_asm.h>
1562306a36Sopenharmony_ci#include <asm/mmu.h>
1662306a36Sopenharmony_ci#include <asm/spectre.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci.macro save_caller_saved_regs_vect
1962306a36Sopenharmony_ci	/* x0 and x1 were saved in the vector entry */
2062306a36Sopenharmony_ci	stp	x2, x3,   [sp, #-16]!
2162306a36Sopenharmony_ci	stp	x4, x5,   [sp, #-16]!
2262306a36Sopenharmony_ci	stp	x6, x7,   [sp, #-16]!
2362306a36Sopenharmony_ci	stp	x8, x9,   [sp, #-16]!
2462306a36Sopenharmony_ci	stp	x10, x11, [sp, #-16]!
2562306a36Sopenharmony_ci	stp	x12, x13, [sp, #-16]!
2662306a36Sopenharmony_ci	stp	x14, x15, [sp, #-16]!
2762306a36Sopenharmony_ci	stp	x16, x17, [sp, #-16]!
2862306a36Sopenharmony_ci.endm
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci.macro restore_caller_saved_regs_vect
3162306a36Sopenharmony_ci	ldp	x16, x17, [sp], #16
3262306a36Sopenharmony_ci	ldp	x14, x15, [sp], #16
3362306a36Sopenharmony_ci	ldp	x12, x13, [sp], #16
3462306a36Sopenharmony_ci	ldp	x10, x11, [sp], #16
3562306a36Sopenharmony_ci	ldp	x8, x9,   [sp], #16
3662306a36Sopenharmony_ci	ldp	x6, x7,   [sp], #16
3762306a36Sopenharmony_ci	ldp	x4, x5,   [sp], #16
3862306a36Sopenharmony_ci	ldp	x2, x3,   [sp], #16
3962306a36Sopenharmony_ci	ldp	x0, x1,   [sp], #16
4062306a36Sopenharmony_ci.endm
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	.text
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciel1_sync:				// Guest trapped into EL2
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	mrs	x0, esr_el2
4762306a36Sopenharmony_ci	ubfx	x0, x0, #ESR_ELx_EC_SHIFT, #ESR_ELx_EC_WIDTH
4862306a36Sopenharmony_ci	cmp	x0, #ESR_ELx_EC_HVC64
4962306a36Sopenharmony_ci	ccmp	x0, #ESR_ELx_EC_HVC32, #4, ne
5062306a36Sopenharmony_ci	b.ne	el1_trap
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * Fastest possible path for ARM_SMCCC_ARCH_WORKAROUND_1.
5462306a36Sopenharmony_ci	 * The workaround has already been applied on the host,
5562306a36Sopenharmony_ci	 * so let's quickly get back to the guest. We don't bother
5662306a36Sopenharmony_ci	 * restoring x1, as it can be clobbered anyway.
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci	ldr	x1, [sp]				// Guest's x0
5962306a36Sopenharmony_ci	eor	w1, w1, #ARM_SMCCC_ARCH_WORKAROUND_1
6062306a36Sopenharmony_ci	cbz	w1, wa_epilogue
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* ARM_SMCCC_ARCH_WORKAROUND_2 handling */
6362306a36Sopenharmony_ci	eor	w1, w1, #(ARM_SMCCC_ARCH_WORKAROUND_1 ^ \
6462306a36Sopenharmony_ci			  ARM_SMCCC_ARCH_WORKAROUND_2)
6562306a36Sopenharmony_ci	cbz	w1, wa_epilogue
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	eor	w1, w1, #(ARM_SMCCC_ARCH_WORKAROUND_2 ^ \
6862306a36Sopenharmony_ci			  ARM_SMCCC_ARCH_WORKAROUND_3)
6962306a36Sopenharmony_ci	cbnz	w1, el1_trap
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciwa_epilogue:
7262306a36Sopenharmony_ci	mov	x0, xzr
7362306a36Sopenharmony_ci	add	sp, sp, #16
7462306a36Sopenharmony_ci	eret
7562306a36Sopenharmony_ci	sb
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciel1_trap:
7862306a36Sopenharmony_ci	get_vcpu_ptr	x1, x0
7962306a36Sopenharmony_ci	mov	x0, #ARM_EXCEPTION_TRAP
8062306a36Sopenharmony_ci	b	__guest_exit
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciel1_irq:
8362306a36Sopenharmony_ciel1_fiq:
8462306a36Sopenharmony_ci	get_vcpu_ptr	x1, x0
8562306a36Sopenharmony_ci	mov	x0, #ARM_EXCEPTION_IRQ
8662306a36Sopenharmony_ci	b	__guest_exit
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciel1_error:
8962306a36Sopenharmony_ci	get_vcpu_ptr	x1, x0
9062306a36Sopenharmony_ci	mov	x0, #ARM_EXCEPTION_EL1_SERROR
9162306a36Sopenharmony_ci	b	__guest_exit
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciel2_sync:
9462306a36Sopenharmony_ci	/* Check for illegal exception return */
9562306a36Sopenharmony_ci	mrs	x0, spsr_el2
9662306a36Sopenharmony_ci	tbnz	x0, #20, 1f
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	save_caller_saved_regs_vect
9962306a36Sopenharmony_ci	stp     x29, x30, [sp, #-16]!
10062306a36Sopenharmony_ci	bl	kvm_unexpected_el2_exception
10162306a36Sopenharmony_ci	ldp     x29, x30, [sp], #16
10262306a36Sopenharmony_ci	restore_caller_saved_regs_vect
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	eret
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci1:
10762306a36Sopenharmony_ci	/* Let's attempt a recovery from the illegal exception return */
10862306a36Sopenharmony_ci	get_vcpu_ptr	x1, x0
10962306a36Sopenharmony_ci	mov	x0, #ARM_EXCEPTION_IL
11062306a36Sopenharmony_ci	b	__guest_exit
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciel2_error:
11462306a36Sopenharmony_ci	save_caller_saved_regs_vect
11562306a36Sopenharmony_ci	stp     x29, x30, [sp, #-16]!
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	bl	kvm_unexpected_el2_exception
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ldp     x29, x30, [sp], #16
12062306a36Sopenharmony_ci	restore_caller_saved_regs_vect
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	eret
12362306a36Sopenharmony_ci	sb
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci.macro invalid_vector	label, target = __guest_exit_panic
12662306a36Sopenharmony_ci	.align	2
12762306a36Sopenharmony_ciSYM_CODE_START_LOCAL(\label)
12862306a36Sopenharmony_ci	b \target
12962306a36Sopenharmony_ciSYM_CODE_END(\label)
13062306a36Sopenharmony_ci.endm
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* None of these should ever happen */
13362306a36Sopenharmony_ci	invalid_vector	el2t_sync_invalid
13462306a36Sopenharmony_ci	invalid_vector	el2t_irq_invalid
13562306a36Sopenharmony_ci	invalid_vector	el2t_fiq_invalid
13662306a36Sopenharmony_ci	invalid_vector	el2t_error_invalid
13762306a36Sopenharmony_ci	invalid_vector	el2h_irq_invalid
13862306a36Sopenharmony_ci	invalid_vector	el2h_fiq_invalid
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	.ltorg
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	.align 11
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci.macro check_preamble_length start, end
14562306a36Sopenharmony_ci/* kvm_patch_vector_branch() generates code that jumps over the preamble. */
14662306a36Sopenharmony_ci.if ((\end-\start) != KVM_VECTOR_PREAMBLE)
14762306a36Sopenharmony_ci	.error "KVM vector preamble length mismatch"
14862306a36Sopenharmony_ci.endif
14962306a36Sopenharmony_ci.endm
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci.macro valid_vect target
15262306a36Sopenharmony_ci	.align 7
15362306a36Sopenharmony_ci661:
15462306a36Sopenharmony_ci	esb
15562306a36Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
15662306a36Sopenharmony_ci662:
15762306a36Sopenharmony_ci	/*
15862306a36Sopenharmony_ci	 * spectre vectors __bp_harden_hyp_vecs generate br instructions at runtime
15962306a36Sopenharmony_ci	 * that jump at offset 8 at __kvm_hyp_vector.
16062306a36Sopenharmony_ci	 * As hyp .text is guarded section, it needs bti j.
16162306a36Sopenharmony_ci	 */
16262306a36Sopenharmony_ci	bti j
16362306a36Sopenharmony_ci	b	\target
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cicheck_preamble_length 661b, 662b
16662306a36Sopenharmony_ci.endm
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci.macro invalid_vect target
16962306a36Sopenharmony_ci	.align 7
17062306a36Sopenharmony_ci661:
17162306a36Sopenharmony_ci	nop
17262306a36Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
17362306a36Sopenharmony_ci662:
17462306a36Sopenharmony_ci	/* Check valid_vect */
17562306a36Sopenharmony_ci	bti j
17662306a36Sopenharmony_ci	b	\target
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cicheck_preamble_length 661b, 662b
17962306a36Sopenharmony_ci.endm
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciSYM_CODE_START(__kvm_hyp_vector)
18262306a36Sopenharmony_ci	invalid_vect	el2t_sync_invalid	// Synchronous EL2t
18362306a36Sopenharmony_ci	invalid_vect	el2t_irq_invalid	// IRQ EL2t
18462306a36Sopenharmony_ci	invalid_vect	el2t_fiq_invalid	// FIQ EL2t
18562306a36Sopenharmony_ci	invalid_vect	el2t_error_invalid	// Error EL2t
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	valid_vect	el2_sync		// Synchronous EL2h
18862306a36Sopenharmony_ci	invalid_vect	el2h_irq_invalid	// IRQ EL2h
18962306a36Sopenharmony_ci	invalid_vect	el2h_fiq_invalid	// FIQ EL2h
19062306a36Sopenharmony_ci	valid_vect	el2_error		// Error EL2h
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	valid_vect	el1_sync		// Synchronous 64-bit EL1
19362306a36Sopenharmony_ci	valid_vect	el1_irq			// IRQ 64-bit EL1
19462306a36Sopenharmony_ci	valid_vect	el1_fiq			// FIQ 64-bit EL1
19562306a36Sopenharmony_ci	valid_vect	el1_error		// Error 64-bit EL1
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	valid_vect	el1_sync		// Synchronous 32-bit EL1
19862306a36Sopenharmony_ci	valid_vect	el1_irq			// IRQ 32-bit EL1
19962306a36Sopenharmony_ci	valid_vect	el1_fiq			// FIQ 32-bit EL1
20062306a36Sopenharmony_ci	valid_vect	el1_error		// Error 32-bit EL1
20162306a36Sopenharmony_ciSYM_CODE_END(__kvm_hyp_vector)
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci.macro spectrev2_smccc_wa1_smc
20462306a36Sopenharmony_ci	sub	sp, sp, #(8 * 4)
20562306a36Sopenharmony_ci	stp	x2, x3, [sp, #(8 * 0)]
20662306a36Sopenharmony_ci	stp	x0, x1, [sp, #(8 * 2)]
20762306a36Sopenharmony_ci	alternative_cb ARM64_ALWAYS_SYSTEM, spectre_bhb_patch_wa3
20862306a36Sopenharmony_ci	/* Patched to mov WA3 when supported */
20962306a36Sopenharmony_ci	mov	w0, #ARM_SMCCC_ARCH_WORKAROUND_1
21062306a36Sopenharmony_ci	alternative_cb_end
21162306a36Sopenharmony_ci	smc	#0
21262306a36Sopenharmony_ci	ldp	x2, x3, [sp, #(8 * 0)]
21362306a36Sopenharmony_ci	add	sp, sp, #(8 * 2)
21462306a36Sopenharmony_ci.endm
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci.macro hyp_ventry	indirect, spectrev2
21762306a36Sopenharmony_ci	.align	7
21862306a36Sopenharmony_ci1:	esb
21962306a36Sopenharmony_ci	.if \spectrev2 != 0
22062306a36Sopenharmony_ci	spectrev2_smccc_wa1_smc
22162306a36Sopenharmony_ci	.else
22262306a36Sopenharmony_ci	stp	x0, x1, [sp, #-16]!
22362306a36Sopenharmony_ci	mitigate_spectre_bhb_loop	x0
22462306a36Sopenharmony_ci	mitigate_spectre_bhb_clear_insn
22562306a36Sopenharmony_ci	.endif
22662306a36Sopenharmony_ci	.if \indirect != 0
22762306a36Sopenharmony_ci	alternative_cb ARM64_ALWAYS_SYSTEM, kvm_patch_vector_branch
22862306a36Sopenharmony_ci	/*
22962306a36Sopenharmony_ci	 * For ARM64_SPECTRE_V3A configurations, these NOPs get replaced with:
23062306a36Sopenharmony_ci	 *
23162306a36Sopenharmony_ci	 * movz	x0, #(addr & 0xffff)
23262306a36Sopenharmony_ci	 * movk	x0, #((addr >> 16) & 0xffff), lsl #16
23362306a36Sopenharmony_ci	 * movk	x0, #((addr >> 32) & 0xffff), lsl #32
23462306a36Sopenharmony_ci	 * br	x0
23562306a36Sopenharmony_ci	 *
23662306a36Sopenharmony_ci	 * Where:
23762306a36Sopenharmony_ci	 * addr = kern_hyp_va(__kvm_hyp_vector) + vector-offset + KVM_VECTOR_PREAMBLE.
23862306a36Sopenharmony_ci	 * See kvm_patch_vector_branch for details.
23962306a36Sopenharmony_ci	 */
24062306a36Sopenharmony_ci	nop
24162306a36Sopenharmony_ci	nop
24262306a36Sopenharmony_ci	nop
24362306a36Sopenharmony_ci	nop
24462306a36Sopenharmony_ci	alternative_cb_end
24562306a36Sopenharmony_ci	.endif
24662306a36Sopenharmony_ci	b	__kvm_hyp_vector + (1b - 0b + KVM_VECTOR_PREAMBLE)
24762306a36Sopenharmony_ci.endm
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci.macro generate_vectors	indirect, spectrev2
25062306a36Sopenharmony_ci0:
25162306a36Sopenharmony_ci	.rept 16
25262306a36Sopenharmony_ci	hyp_ventry	\indirect, \spectrev2
25362306a36Sopenharmony_ci	.endr
25462306a36Sopenharmony_ci	.org 0b + SZ_2K		// Safety measure
25562306a36Sopenharmony_ci.endm
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	.align	11
25862306a36Sopenharmony_ciSYM_CODE_START(__bp_harden_hyp_vecs)
25962306a36Sopenharmony_ci	generate_vectors indirect = 0, spectrev2 = 1 // HYP_VECTOR_SPECTRE_DIRECT
26062306a36Sopenharmony_ci	generate_vectors indirect = 1, spectrev2 = 0 // HYP_VECTOR_INDIRECT
26162306a36Sopenharmony_ci	generate_vectors indirect = 1, spectrev2 = 1 // HYP_VECTOR_SPECTRE_INDIRECT
26262306a36Sopenharmony_ci1:	.org __bp_harden_hyp_vecs + __BP_HARDEN_HYP_VECS_SZ
26362306a36Sopenharmony_ci	.org 1b
26462306a36Sopenharmony_ciSYM_CODE_END(__bp_harden_hyp_vecs)
265