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