18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 ARM Ltd.
48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
88c2ecf20Sopenharmony_ci#include <linux/random.h>
98c2ecf20Sopenharmony_ci#include <linux/memblock.h>
108c2ecf20Sopenharmony_ci#include <asm/alternative.h>
118c2ecf20Sopenharmony_ci#include <asm/debug-monitors.h>
128c2ecf20Sopenharmony_ci#include <asm/insn.h>
138c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * The LSB of the HYP VA tag
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_cistatic u8 tag_lsb;
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * The HYP VA tag value with the region bit
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistatic u64 tag_val;
238c2ecf20Sopenharmony_cistatic u64 va_mask;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * We want to generate a hyp VA with the following format (with V ==
278c2ecf20Sopenharmony_ci * vabits_actual):
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci *  63 ... V |     V-1    | V-2 .. tag_lsb | tag_lsb - 1 .. 0
308c2ecf20Sopenharmony_ci *  ---------------------------------------------------------
318c2ecf20Sopenharmony_ci * | 0000000 | hyp_va_msb |   random tag   |  kern linear VA |
328c2ecf20Sopenharmony_ci *           |--------- tag_val -----------|----- va_mask ---|
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * which does not conflict with the idmap regions.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci__init void kvm_compute_layout(void)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	phys_addr_t idmap_addr = __pa_symbol(__hyp_idmap_text_start);
398c2ecf20Sopenharmony_ci	u64 hyp_va_msb;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Where is my RAM region? */
428c2ecf20Sopenharmony_ci	hyp_va_msb  = idmap_addr & BIT(vabits_actual - 1);
438c2ecf20Sopenharmony_ci	hyp_va_msb ^= BIT(vabits_actual - 1);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	tag_lsb = fls64((u64)phys_to_virt(memblock_start_of_DRAM()) ^
468c2ecf20Sopenharmony_ci			(u64)(high_memory - 1));
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	va_mask = GENMASK_ULL(tag_lsb - 1, 0);
498c2ecf20Sopenharmony_ci	tag_val = hyp_va_msb;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && tag_lsb != (vabits_actual - 1)) {
528c2ecf20Sopenharmony_ci		/* We have some free bits to insert a random tag. */
538c2ecf20Sopenharmony_ci		tag_val |= get_random_long() & GENMASK_ULL(vabits_actual - 2, tag_lsb);
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci	tag_val >>= tag_lsb;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic u32 compute_instruction(int n, u32 rd, u32 rn)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	u32 insn = AARCH64_BREAK_FAULT;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	switch (n) {
638c2ecf20Sopenharmony_ci	case 0:
648c2ecf20Sopenharmony_ci		insn = aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_AND,
658c2ecf20Sopenharmony_ci							  AARCH64_INSN_VARIANT_64BIT,
668c2ecf20Sopenharmony_ci							  rn, rd, va_mask);
678c2ecf20Sopenharmony_ci		break;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	case 1:
708c2ecf20Sopenharmony_ci		/* ROR is a variant of EXTR with Rm = Rn */
718c2ecf20Sopenharmony_ci		insn = aarch64_insn_gen_extr(AARCH64_INSN_VARIANT_64BIT,
728c2ecf20Sopenharmony_ci					     rn, rn, rd,
738c2ecf20Sopenharmony_ci					     tag_lsb);
748c2ecf20Sopenharmony_ci		break;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	case 2:
778c2ecf20Sopenharmony_ci		insn = aarch64_insn_gen_add_sub_imm(rd, rn,
788c2ecf20Sopenharmony_ci						    tag_val & GENMASK(11, 0),
798c2ecf20Sopenharmony_ci						    AARCH64_INSN_VARIANT_64BIT,
808c2ecf20Sopenharmony_ci						    AARCH64_INSN_ADSB_ADD);
818c2ecf20Sopenharmony_ci		break;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	case 3:
848c2ecf20Sopenharmony_ci		insn = aarch64_insn_gen_add_sub_imm(rd, rn,
858c2ecf20Sopenharmony_ci						    tag_val & GENMASK(23, 12),
868c2ecf20Sopenharmony_ci						    AARCH64_INSN_VARIANT_64BIT,
878c2ecf20Sopenharmony_ci						    AARCH64_INSN_ADSB_ADD);
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	case 4:
918c2ecf20Sopenharmony_ci		/* ROR is a variant of EXTR with Rm = Rn */
928c2ecf20Sopenharmony_ci		insn = aarch64_insn_gen_extr(AARCH64_INSN_VARIANT_64BIT,
938c2ecf20Sopenharmony_ci					     rn, rn, rd, 64 - tag_lsb);
948c2ecf20Sopenharmony_ci		break;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return insn;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_civoid __init kvm_update_va_mask(struct alt_instr *alt,
1018c2ecf20Sopenharmony_ci			       __le32 *origptr, __le32 *updptr, int nr_inst)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int i;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	BUG_ON(nr_inst != 5);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	for (i = 0; i < nr_inst; i++) {
1088c2ecf20Sopenharmony_ci		u32 rd, rn, insn, oinsn;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		/*
1118c2ecf20Sopenharmony_ci		 * VHE doesn't need any address translation, let's NOP
1128c2ecf20Sopenharmony_ci		 * everything.
1138c2ecf20Sopenharmony_ci		 *
1148c2ecf20Sopenharmony_ci		 * Alternatively, if the tag is zero (because the layout
1158c2ecf20Sopenharmony_ci		 * dictates it and we don't have any spare bits in the
1168c2ecf20Sopenharmony_ci		 * address), NOP everything after masking the kernel VA.
1178c2ecf20Sopenharmony_ci		 */
1188c2ecf20Sopenharmony_ci		if (has_vhe() || (!tag_val && i > 0)) {
1198c2ecf20Sopenharmony_ci			updptr[i] = cpu_to_le32(aarch64_insn_gen_nop());
1208c2ecf20Sopenharmony_ci			continue;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		oinsn = le32_to_cpu(origptr[i]);
1248c2ecf20Sopenharmony_ci		rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn);
1258c2ecf20Sopenharmony_ci		rn = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RN, oinsn);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		insn = compute_instruction(i, rd, rn);
1288c2ecf20Sopenharmony_ci		BUG_ON(insn == AARCH64_BREAK_FAULT);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		updptr[i] = cpu_to_le32(insn);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_civoid *__kvm_bp_vect_base;
1358c2ecf20Sopenharmony_ciint __kvm_harden_el2_vector_slot;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_civoid kvm_patch_vector_branch(struct alt_instr *alt,
1388c2ecf20Sopenharmony_ci			     __le32 *origptr, __le32 *updptr, int nr_inst)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	u64 addr;
1418c2ecf20Sopenharmony_ci	u32 insn;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	BUG_ON(nr_inst != 5);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (has_vhe() || !cpus_have_const_cap(ARM64_HARDEN_EL2_VECTORS)) {
1468c2ecf20Sopenharmony_ci		WARN_ON_ONCE(cpus_have_const_cap(ARM64_HARDEN_EL2_VECTORS));
1478c2ecf20Sopenharmony_ci		return;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/*
1518c2ecf20Sopenharmony_ci	 * Compute HYP VA by using the same computation as kern_hyp_va()
1528c2ecf20Sopenharmony_ci	 */
1538c2ecf20Sopenharmony_ci	addr = (uintptr_t)kvm_ksym_ref(__kvm_hyp_vector);
1548c2ecf20Sopenharmony_ci	addr &= va_mask;
1558c2ecf20Sopenharmony_ci	addr |= tag_val << tag_lsb;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* Use PC[10:7] to branch to the same vector in KVM */
1588c2ecf20Sopenharmony_ci	addr |= ((u64)origptr & GENMASK_ULL(10, 7));
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/*
1618c2ecf20Sopenharmony_ci	 * Branch over the preamble in order to avoid the initial store on
1628c2ecf20Sopenharmony_ci	 * the stack (which we already perform in the hardening vectors).
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci	addr += KVM_VECTOR_PREAMBLE;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* stp x0, x1, [sp, #-16]! */
1678c2ecf20Sopenharmony_ci	insn = aarch64_insn_gen_load_store_pair(AARCH64_INSN_REG_0,
1688c2ecf20Sopenharmony_ci						AARCH64_INSN_REG_1,
1698c2ecf20Sopenharmony_ci						AARCH64_INSN_REG_SP,
1708c2ecf20Sopenharmony_ci						-16,
1718c2ecf20Sopenharmony_ci						AARCH64_INSN_VARIANT_64BIT,
1728c2ecf20Sopenharmony_ci						AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX);
1738c2ecf20Sopenharmony_ci	*updptr++ = cpu_to_le32(insn);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* movz x0, #(addr & 0xffff) */
1768c2ecf20Sopenharmony_ci	insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0,
1778c2ecf20Sopenharmony_ci					 (u16)addr,
1788c2ecf20Sopenharmony_ci					 0,
1798c2ecf20Sopenharmony_ci					 AARCH64_INSN_VARIANT_64BIT,
1808c2ecf20Sopenharmony_ci					 AARCH64_INSN_MOVEWIDE_ZERO);
1818c2ecf20Sopenharmony_ci	*updptr++ = cpu_to_le32(insn);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* movk x0, #((addr >> 16) & 0xffff), lsl #16 */
1848c2ecf20Sopenharmony_ci	insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0,
1858c2ecf20Sopenharmony_ci					 (u16)(addr >> 16),
1868c2ecf20Sopenharmony_ci					 16,
1878c2ecf20Sopenharmony_ci					 AARCH64_INSN_VARIANT_64BIT,
1888c2ecf20Sopenharmony_ci					 AARCH64_INSN_MOVEWIDE_KEEP);
1898c2ecf20Sopenharmony_ci	*updptr++ = cpu_to_le32(insn);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* movk x0, #((addr >> 32) & 0xffff), lsl #32 */
1928c2ecf20Sopenharmony_ci	insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0,
1938c2ecf20Sopenharmony_ci					 (u16)(addr >> 32),
1948c2ecf20Sopenharmony_ci					 32,
1958c2ecf20Sopenharmony_ci					 AARCH64_INSN_VARIANT_64BIT,
1968c2ecf20Sopenharmony_ci					 AARCH64_INSN_MOVEWIDE_KEEP);
1978c2ecf20Sopenharmony_ci	*updptr++ = cpu_to_le32(insn);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* br x0 */
2008c2ecf20Sopenharmony_ci	insn = aarch64_insn_gen_branch_reg(AARCH64_INSN_REG_0,
2018c2ecf20Sopenharmony_ci					   AARCH64_INSN_BRANCH_NOLINK);
2028c2ecf20Sopenharmony_ci	*updptr++ = cpu_to_le32(insn);
2038c2ecf20Sopenharmony_ci}
204