162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 ARM Ltd. 462306a36Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kvm_host.h> 862306a36Sopenharmony_ci#include <linux/random.h> 962306a36Sopenharmony_ci#include <linux/memblock.h> 1062306a36Sopenharmony_ci#include <asm/alternative.h> 1162306a36Sopenharmony_ci#include <asm/debug-monitors.h> 1262306a36Sopenharmony_ci#include <asm/insn.h> 1362306a36Sopenharmony_ci#include <asm/kvm_mmu.h> 1462306a36Sopenharmony_ci#include <asm/memory.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * The LSB of the HYP VA tag 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_cistatic u8 tag_lsb; 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * The HYP VA tag value with the region bit 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic u64 tag_val; 2462306a36Sopenharmony_cistatic u64 va_mask; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * Compute HYP VA by using the same computation as kern_hyp_va(). 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic u64 __early_kern_hyp_va(u64 addr) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci addr &= va_mask; 3262306a36Sopenharmony_ci addr |= tag_val << tag_lsb; 3362306a36Sopenharmony_ci return addr; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Store a hyp VA <-> PA offset into a EL2-owned variable. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic void init_hyp_physvirt_offset(void) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci u64 kern_va, hyp_va; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* Compute the offset from the hyp VA and PA of a random symbol. */ 4462306a36Sopenharmony_ci kern_va = (u64)lm_alias(__hyp_text_start); 4562306a36Sopenharmony_ci hyp_va = __early_kern_hyp_va(kern_va); 4662306a36Sopenharmony_ci hyp_physvirt_offset = (s64)__pa(kern_va) - (s64)hyp_va; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * We want to generate a hyp VA with the following format (with V == 5162306a36Sopenharmony_ci * vabits_actual): 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * 63 ... V | V-1 | V-2 .. tag_lsb | tag_lsb - 1 .. 0 5462306a36Sopenharmony_ci * --------------------------------------------------------- 5562306a36Sopenharmony_ci * | 0000000 | hyp_va_msb | random tag | kern linear VA | 5662306a36Sopenharmony_ci * |--------- tag_val -----------|----- va_mask ---| 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * which does not conflict with the idmap regions. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci__init void kvm_compute_layout(void) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci phys_addr_t idmap_addr = __pa_symbol(__hyp_idmap_text_start); 6362306a36Sopenharmony_ci u64 hyp_va_msb; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Where is my RAM region? */ 6662306a36Sopenharmony_ci hyp_va_msb = idmap_addr & BIT(vabits_actual - 1); 6762306a36Sopenharmony_ci hyp_va_msb ^= BIT(vabits_actual - 1); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci tag_lsb = fls64((u64)phys_to_virt(memblock_start_of_DRAM()) ^ 7062306a36Sopenharmony_ci (u64)(high_memory - 1)); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci va_mask = GENMASK_ULL(tag_lsb - 1, 0); 7362306a36Sopenharmony_ci tag_val = hyp_va_msb; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && tag_lsb != (vabits_actual - 1)) { 7662306a36Sopenharmony_ci /* We have some free bits to insert a random tag. */ 7762306a36Sopenharmony_ci tag_val |= get_random_long() & GENMASK_ULL(vabits_actual - 2, tag_lsb); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci tag_val >>= tag_lsb; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci init_hyp_physvirt_offset(); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * The .hyp.reloc ELF section contains a list of kimg positions that 8662306a36Sopenharmony_ci * contains kimg VAs but will be accessed only in hyp execution context. 8762306a36Sopenharmony_ci * Convert them to hyp VAs. See gen-hyprel.c for more details. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci__init void kvm_apply_hyp_relocations(void) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int32_t *rel; 9262306a36Sopenharmony_ci int32_t *begin = (int32_t *)__hyp_reloc_begin; 9362306a36Sopenharmony_ci int32_t *end = (int32_t *)__hyp_reloc_end; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (rel = begin; rel < end; ++rel) { 9662306a36Sopenharmony_ci uintptr_t *ptr, kimg_va; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * Each entry contains a 32-bit relative offset from itself 10062306a36Sopenharmony_ci * to a kimg VA position. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci ptr = (uintptr_t *)lm_alias((char *)rel + *rel); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Read the kimg VA value at the relocation address. */ 10562306a36Sopenharmony_ci kimg_va = *ptr; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Convert to hyp VA and store back to the relocation address. */ 10862306a36Sopenharmony_ci *ptr = __early_kern_hyp_va((uintptr_t)lm_alias(kimg_va)); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic u32 compute_instruction(int n, u32 rd, u32 rn) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 insn = AARCH64_BREAK_FAULT; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci switch (n) { 11762306a36Sopenharmony_ci case 0: 11862306a36Sopenharmony_ci insn = aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_AND, 11962306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 12062306a36Sopenharmony_ci rn, rd, va_mask); 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci case 1: 12462306a36Sopenharmony_ci /* ROR is a variant of EXTR with Rm = Rn */ 12562306a36Sopenharmony_ci insn = aarch64_insn_gen_extr(AARCH64_INSN_VARIANT_64BIT, 12662306a36Sopenharmony_ci rn, rn, rd, 12762306a36Sopenharmony_ci tag_lsb); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci case 2: 13162306a36Sopenharmony_ci insn = aarch64_insn_gen_add_sub_imm(rd, rn, 13262306a36Sopenharmony_ci tag_val & GENMASK(11, 0), 13362306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 13462306a36Sopenharmony_ci AARCH64_INSN_ADSB_ADD); 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci case 3: 13862306a36Sopenharmony_ci insn = aarch64_insn_gen_add_sub_imm(rd, rn, 13962306a36Sopenharmony_ci tag_val & GENMASK(23, 12), 14062306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 14162306a36Sopenharmony_ci AARCH64_INSN_ADSB_ADD); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci case 4: 14562306a36Sopenharmony_ci /* ROR is a variant of EXTR with Rm = Rn */ 14662306a36Sopenharmony_ci insn = aarch64_insn_gen_extr(AARCH64_INSN_VARIANT_64BIT, 14762306a36Sopenharmony_ci rn, rn, rd, 64 - tag_lsb); 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return insn; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_civoid __init kvm_update_va_mask(struct alt_instr *alt, 15562306a36Sopenharmony_ci __le32 *origptr, __le32 *updptr, int nr_inst) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int i; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci BUG_ON(nr_inst != 5); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = 0; i < nr_inst; i++) { 16262306a36Sopenharmony_ci u32 rd, rn, insn, oinsn; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * VHE doesn't need any address translation, let's NOP 16662306a36Sopenharmony_ci * everything. 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * Alternatively, if the tag is zero (because the layout 16962306a36Sopenharmony_ci * dictates it and we don't have any spare bits in the 17062306a36Sopenharmony_ci * address), NOP everything after masking the kernel VA. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci if (cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN) || (!tag_val && i > 0)) { 17362306a36Sopenharmony_ci updptr[i] = cpu_to_le32(aarch64_insn_gen_nop()); 17462306a36Sopenharmony_ci continue; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci oinsn = le32_to_cpu(origptr[i]); 17862306a36Sopenharmony_ci rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn); 17962306a36Sopenharmony_ci rn = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RN, oinsn); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci insn = compute_instruction(i, rd, rn); 18262306a36Sopenharmony_ci BUG_ON(insn == AARCH64_BREAK_FAULT); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci updptr[i] = cpu_to_le32(insn); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_civoid kvm_patch_vector_branch(struct alt_instr *alt, 18962306a36Sopenharmony_ci __le32 *origptr, __le32 *updptr, int nr_inst) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci u64 addr; 19262306a36Sopenharmony_ci u32 insn; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci BUG_ON(nr_inst != 4); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!cpus_have_cap(ARM64_SPECTRE_V3A) || 19762306a36Sopenharmony_ci WARN_ON_ONCE(cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN))) 19862306a36Sopenharmony_ci return; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * Compute HYP VA by using the same computation as kern_hyp_va() 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci addr = __early_kern_hyp_va((u64)kvm_ksym_ref(__kvm_hyp_vector)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Use PC[10:7] to branch to the same vector in KVM */ 20662306a36Sopenharmony_ci addr |= ((u64)origptr & GENMASK_ULL(10, 7)); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * Branch over the preamble in order to avoid the initial store on 21062306a36Sopenharmony_ci * the stack (which we already perform in the hardening vectors). 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci addr += KVM_VECTOR_PREAMBLE; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* movz x0, #(addr & 0xffff) */ 21562306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0, 21662306a36Sopenharmony_ci (u16)addr, 21762306a36Sopenharmony_ci 0, 21862306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 21962306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_ZERO); 22062306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* movk x0, #((addr >> 16) & 0xffff), lsl #16 */ 22362306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0, 22462306a36Sopenharmony_ci (u16)(addr >> 16), 22562306a36Sopenharmony_ci 16, 22662306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 22762306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_KEEP); 22862306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* movk x0, #((addr >> 32) & 0xffff), lsl #32 */ 23162306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0, 23262306a36Sopenharmony_ci (u16)(addr >> 32), 23362306a36Sopenharmony_ci 32, 23462306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 23562306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_KEEP); 23662306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* br x0 */ 23962306a36Sopenharmony_ci insn = aarch64_insn_gen_branch_reg(AARCH64_INSN_REG_0, 24062306a36Sopenharmony_ci AARCH64_INSN_BRANCH_NOLINK); 24162306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void generate_mov_q(u64 val, __le32 *origptr, __le32 *updptr, int nr_inst) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci u32 insn, oinsn, rd; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci BUG_ON(nr_inst != 4); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Compute target register */ 25162306a36Sopenharmony_ci oinsn = le32_to_cpu(*origptr); 25262306a36Sopenharmony_ci rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* movz rd, #(val & 0xffff) */ 25562306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(rd, 25662306a36Sopenharmony_ci (u16)val, 25762306a36Sopenharmony_ci 0, 25862306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 25962306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_ZERO); 26062306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* movk rd, #((val >> 16) & 0xffff), lsl #16 */ 26362306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(rd, 26462306a36Sopenharmony_ci (u16)(val >> 16), 26562306a36Sopenharmony_ci 16, 26662306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 26762306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_KEEP); 26862306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* movk rd, #((val >> 32) & 0xffff), lsl #32 */ 27162306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(rd, 27262306a36Sopenharmony_ci (u16)(val >> 32), 27362306a36Sopenharmony_ci 32, 27462306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 27562306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_KEEP); 27662306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* movk rd, #((val >> 48) & 0xffff), lsl #48 */ 27962306a36Sopenharmony_ci insn = aarch64_insn_gen_movewide(rd, 28062306a36Sopenharmony_ci (u16)(val >> 48), 28162306a36Sopenharmony_ci 48, 28262306a36Sopenharmony_ci AARCH64_INSN_VARIANT_64BIT, 28362306a36Sopenharmony_ci AARCH64_INSN_MOVEWIDE_KEEP); 28462306a36Sopenharmony_ci *updptr++ = cpu_to_le32(insn); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_civoid kvm_get_kimage_voffset(struct alt_instr *alt, 28862306a36Sopenharmony_ci __le32 *origptr, __le32 *updptr, int nr_inst) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci generate_mov_q(kimage_voffset, origptr, updptr, nr_inst); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_civoid kvm_compute_final_ctr_el0(struct alt_instr *alt, 29462306a36Sopenharmony_ci __le32 *origptr, __le32 *updptr, int nr_inst) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci generate_mov_q(read_sanitised_ftr_reg(SYS_CTR_EL0), 29762306a36Sopenharmony_ci origptr, updptr, nr_inst); 29862306a36Sopenharmony_ci} 299