mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
Merge branch 'kvm-arm64/vector-rework' into kvmarm-master/next
Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
dc2286f397
13 changed files with 222 additions and 267 deletions
|
@ -100,7 +100,7 @@ hypervisor maps kernel pages in EL2 at a fixed (and potentially
|
||||||
random) offset from the linear mapping. See the kern_hyp_va macro and
|
random) offset from the linear mapping. See the kern_hyp_va macro and
|
||||||
kvm_update_va_mask function for more details. MMIO devices such as
|
kvm_update_va_mask function for more details. MMIO devices such as
|
||||||
GICv2 gets mapped next to the HYP idmap page, as do vectors when
|
GICv2 gets mapped next to the HYP idmap page, as do vectors when
|
||||||
ARM64_HARDEN_EL2_VECTORS is selected for particular CPUs.
|
ARM64_SPECTRE_V3A is enabled for particular CPUs.
|
||||||
|
|
||||||
When using KVM with the Virtualization Host Extensions, no additional
|
When using KVM with the Virtualization Host Extensions, no additional
|
||||||
mappings are created, since the host kernel runs directly in EL2.
|
mappings are created, since the host kernel runs directly in EL2.
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#define ARM64_HAS_VIRT_HOST_EXTN 11
|
#define ARM64_HAS_VIRT_HOST_EXTN 11
|
||||||
#define ARM64_WORKAROUND_CAVIUM_27456 12
|
#define ARM64_WORKAROUND_CAVIUM_27456 12
|
||||||
#define ARM64_HAS_32BIT_EL0 13
|
#define ARM64_HAS_32BIT_EL0 13
|
||||||
#define ARM64_HARDEN_EL2_VECTORS 14
|
#define ARM64_SPECTRE_V3A 14
|
||||||
#define ARM64_HAS_CNP 15
|
#define ARM64_HAS_CNP 15
|
||||||
#define ARM64_HAS_NO_FPSIMD 16
|
#define ARM64_HAS_NO_FPSIMD 16
|
||||||
#define ARM64_WORKAROUND_REPEAT_TLBI 17
|
#define ARM64_WORKAROUND_REPEAT_TLBI 17
|
||||||
|
|
|
@ -34,8 +34,6 @@
|
||||||
*/
|
*/
|
||||||
#define KVM_VECTOR_PREAMBLE (2 * AARCH64_INSN_SIZE)
|
#define KVM_VECTOR_PREAMBLE (2 * AARCH64_INSN_SIZE)
|
||||||
|
|
||||||
#define __SMCCC_WORKAROUND_1_SMC_SZ 36
|
|
||||||
|
|
||||||
#define KVM_HOST_SMCCC_ID(id) \
|
#define KVM_HOST_SMCCC_ID(id) \
|
||||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||||
ARM_SMCCC_SMC_64, \
|
ARM_SMCCC_SMC_64, \
|
||||||
|
@ -175,7 +173,6 @@ extern unsigned long kvm_arm_hyp_percpu_base[NR_CPUS];
|
||||||
DECLARE_KVM_NVHE_SYM(__per_cpu_start);
|
DECLARE_KVM_NVHE_SYM(__per_cpu_start);
|
||||||
DECLARE_KVM_NVHE_SYM(__per_cpu_end);
|
DECLARE_KVM_NVHE_SYM(__per_cpu_end);
|
||||||
|
|
||||||
extern atomic_t arm64_el2_vector_last_slot;
|
|
||||||
DECLARE_KVM_HYP_SYM(__bp_harden_hyp_vecs);
|
DECLARE_KVM_HYP_SYM(__bp_harden_hyp_vecs);
|
||||||
#define __bp_harden_hyp_vecs CHOOSE_HYP_SYM(__bp_harden_hyp_vecs)
|
#define __bp_harden_hyp_vecs CHOOSE_HYP_SYM(__bp_harden_hyp_vecs)
|
||||||
|
|
||||||
|
@ -196,8 +193,6 @@ extern void __vgic_v3_init_lrs(void);
|
||||||
|
|
||||||
extern u32 __kvm_get_mdcr_el2(void);
|
extern u32 __kvm_get_mdcr_el2(void);
|
||||||
|
|
||||||
extern char __smccc_workaround_1_smc[__SMCCC_WORKAROUND_1_SMC_SZ];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Obtain the PC-relative address of a kernel symbol
|
* Obtain the PC-relative address of a kernel symbol
|
||||||
* s: symbol
|
* s: symbol
|
||||||
|
|
|
@ -248,52 +248,6 @@ static inline int kvm_write_guest_lock(struct kvm *kvm, gpa_t gpa,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* EL2 vectors can be mapped and rerouted in a number of ways,
|
|
||||||
* depending on the kernel configuration and CPU present:
|
|
||||||
*
|
|
||||||
* - If the CPU is affected by Spectre-v2, the hardening sequence is
|
|
||||||
* placed in one of the vector slots, which is executed before jumping
|
|
||||||
* to the real vectors.
|
|
||||||
*
|
|
||||||
* - If the CPU also has the ARM64_HARDEN_EL2_VECTORS cap, the slot
|
|
||||||
* containing the hardening sequence is mapped next to the idmap page,
|
|
||||||
* and executed before jumping to the real vectors.
|
|
||||||
*
|
|
||||||
* - If the CPU only has the ARM64_HARDEN_EL2_VECTORS cap, then an
|
|
||||||
* empty slot is selected, mapped next to the idmap page, and
|
|
||||||
* executed before jumping to the real vectors.
|
|
||||||
*
|
|
||||||
* Note that ARM64_HARDEN_EL2_VECTORS is somewhat incompatible with
|
|
||||||
* VHE, as we don't have hypervisor-specific mappings. If the system
|
|
||||||
* is VHE and yet selects this capability, it will be ignored.
|
|
||||||
*/
|
|
||||||
extern void *__kvm_bp_vect_base;
|
|
||||||
extern int __kvm_harden_el2_vector_slot;
|
|
||||||
|
|
||||||
static inline void *kvm_get_hyp_vector(void)
|
|
||||||
{
|
|
||||||
struct bp_hardening_data *data = arm64_get_bp_hardening_data();
|
|
||||||
void *vect = kern_hyp_va(kvm_ksym_ref(__kvm_hyp_vector));
|
|
||||||
int slot = -1;
|
|
||||||
|
|
||||||
if (cpus_have_const_cap(ARM64_SPECTRE_V2) && data->fn) {
|
|
||||||
vect = kern_hyp_va(kvm_ksym_ref(__bp_harden_hyp_vecs));
|
|
||||||
slot = data->hyp_vectors_slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this_cpu_has_cap(ARM64_HARDEN_EL2_VECTORS) && !has_vhe()) {
|
|
||||||
vect = __kvm_bp_vect_base;
|
|
||||||
if (slot == -1)
|
|
||||||
slot = __kvm_harden_el2_vector_slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot != -1)
|
|
||||||
vect += slot * SZ_2K;
|
|
||||||
|
|
||||||
return vect;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define kvm_phys_to_vttbr(addr) phys_to_ttbr(addr)
|
#define kvm_phys_to_vttbr(addr) phys_to_ttbr(addr)
|
||||||
|
|
||||||
static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu)
|
static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu)
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
#define USER_ASID_FLAG (UL(1) << USER_ASID_BIT)
|
#define USER_ASID_FLAG (UL(1) << USER_ASID_BIT)
|
||||||
#define TTBR_ASID_MASK (UL(0xffff) << 48)
|
#define TTBR_ASID_MASK (UL(0xffff) << 48)
|
||||||
|
|
||||||
#define BP_HARDEN_EL2_SLOTS 4
|
|
||||||
#define __BP_HARDEN_HYP_VECS_SZ (BP_HARDEN_EL2_SLOTS * SZ_2K)
|
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
#include <linux/refcount.h>
|
#include <linux/refcount.h>
|
||||||
|
@ -41,32 +38,6 @@ static inline bool arm64_kernel_unmapped_at_el0(void)
|
||||||
return cpus_have_const_cap(ARM64_UNMAP_KERNEL_AT_EL0);
|
return cpus_have_const_cap(ARM64_UNMAP_KERNEL_AT_EL0);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void (*bp_hardening_cb_t)(void);
|
|
||||||
|
|
||||||
struct bp_hardening_data {
|
|
||||||
int hyp_vectors_slot;
|
|
||||||
bp_hardening_cb_t fn;
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data);
|
|
||||||
|
|
||||||
static inline struct bp_hardening_data *arm64_get_bp_hardening_data(void)
|
|
||||||
{
|
|
||||||
return this_cpu_ptr(&bp_hardening_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void arm64_apply_bp_hardening(void)
|
|
||||||
{
|
|
||||||
struct bp_hardening_data *d;
|
|
||||||
|
|
||||||
if (!cpus_have_const_cap(ARM64_SPECTRE_V2))
|
|
||||||
return;
|
|
||||||
|
|
||||||
d = arm64_get_bp_hardening_data();
|
|
||||||
if (d->fn)
|
|
||||||
d->fn();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void arm64_memblock_init(void);
|
extern void arm64_memblock_init(void);
|
||||||
extern void paging_init(void);
|
extern void paging_init(void);
|
||||||
extern void bootmem_init(void);
|
extern void bootmem_init(void);
|
||||||
|
|
|
@ -9,7 +9,15 @@
|
||||||
#ifndef __ASM_SPECTRE_H
|
#ifndef __ASM_SPECTRE_H
|
||||||
#define __ASM_SPECTRE_H
|
#define __ASM_SPECTRE_H
|
||||||
|
|
||||||
|
#define BP_HARDEN_EL2_SLOTS 4
|
||||||
|
#define __BP_HARDEN_HYP_VECS_SZ ((BP_HARDEN_EL2_SLOTS - 1) * SZ_2K)
|
||||||
|
|
||||||
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
|
#include <linux/percpu.h>
|
||||||
|
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
|
#include <asm/virt.h>
|
||||||
|
|
||||||
/* Watch out, ordering is important here. */
|
/* Watch out, ordering is important here. */
|
||||||
enum mitigation_state {
|
enum mitigation_state {
|
||||||
|
@ -20,13 +28,68 @@ enum mitigation_state {
|
||||||
|
|
||||||
struct task_struct;
|
struct task_struct;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: the order of this enum corresponds to __bp_harden_hyp_vecs and
|
||||||
|
* we rely on having the direct vectors first.
|
||||||
|
*/
|
||||||
|
enum arm64_hyp_spectre_vector {
|
||||||
|
/*
|
||||||
|
* Take exceptions directly to __kvm_hyp_vector. This must be
|
||||||
|
* 0 so that it used by default when mitigations are not needed.
|
||||||
|
*/
|
||||||
|
HYP_VECTOR_DIRECT,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bounce via a slot in the hypervisor text mapping of
|
||||||
|
* __bp_harden_hyp_vecs, which contains an SMC call.
|
||||||
|
*/
|
||||||
|
HYP_VECTOR_SPECTRE_DIRECT,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bounce via a slot in a special mapping of __bp_harden_hyp_vecs
|
||||||
|
* next to the idmap page.
|
||||||
|
*/
|
||||||
|
HYP_VECTOR_INDIRECT,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bounce via a slot in a special mapping of __bp_harden_hyp_vecs
|
||||||
|
* next to the idmap page, which contains an SMC call.
|
||||||
|
*/
|
||||||
|
HYP_VECTOR_SPECTRE_INDIRECT,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*bp_hardening_cb_t)(void);
|
||||||
|
|
||||||
|
struct bp_hardening_data {
|
||||||
|
enum arm64_hyp_spectre_vector slot;
|
||||||
|
bp_hardening_cb_t fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data);
|
||||||
|
|
||||||
|
static inline void arm64_apply_bp_hardening(void)
|
||||||
|
{
|
||||||
|
struct bp_hardening_data *d;
|
||||||
|
|
||||||
|
if (!cpus_have_const_cap(ARM64_SPECTRE_V2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
d = this_cpu_ptr(&bp_hardening_data);
|
||||||
|
if (d->fn)
|
||||||
|
d->fn();
|
||||||
|
}
|
||||||
|
|
||||||
enum mitigation_state arm64_get_spectre_v2_state(void);
|
enum mitigation_state arm64_get_spectre_v2_state(void);
|
||||||
bool has_spectre_v2(const struct arm64_cpu_capabilities *cap, int scope);
|
bool has_spectre_v2(const struct arm64_cpu_capabilities *cap, int scope);
|
||||||
void spectre_v2_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
|
void spectre_v2_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
|
||||||
|
|
||||||
|
bool has_spectre_v3a(const struct arm64_cpu_capabilities *cap, int scope);
|
||||||
|
void spectre_v3a_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
|
||||||
|
|
||||||
enum mitigation_state arm64_get_spectre_v4_state(void);
|
enum mitigation_state arm64_get_spectre_v4_state(void);
|
||||||
bool has_spectre_v4(const struct arm64_cpu_capabilities *cap, int scope);
|
bool has_spectre_v4(const struct arm64_cpu_capabilities *cap, int scope);
|
||||||
void spectre_v4_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
|
void spectre_v4_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
|
||||||
void spectre_v4_enable_task_mitigation(struct task_struct *tsk);
|
void spectre_v4_enable_task_mitigation(struct task_struct *tsk);
|
||||||
|
|
||||||
|
#endif /* __ASSEMBLY__ */
|
||||||
#endif /* __ASM_SPECTRE_H */
|
#endif /* __ASM_SPECTRE_H */
|
||||||
|
|
|
@ -196,16 +196,6 @@ has_neoverse_n1_erratum_1542419(const struct arm64_cpu_capabilities *entry,
|
||||||
return is_midr_in_range(midr, &range) && has_dic;
|
return is_midr_in_range(midr, &range) && has_dic;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_RANDOMIZE_BASE
|
|
||||||
|
|
||||||
static const struct midr_range ca57_a72[] = {
|
|
||||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
|
|
||||||
MIDR_ALL_VERSIONS(MIDR_CORTEX_A72),
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI
|
#ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI
|
||||||
static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = {
|
static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = {
|
||||||
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1009
|
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1009
|
||||||
|
@ -461,9 +451,12 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
|
||||||
},
|
},
|
||||||
#ifdef CONFIG_RANDOMIZE_BASE
|
#ifdef CONFIG_RANDOMIZE_BASE
|
||||||
{
|
{
|
||||||
.desc = "EL2 vector hardening",
|
/* Must come after the Spectre-v2 entry */
|
||||||
.capability = ARM64_HARDEN_EL2_VECTORS,
|
.desc = "Spectre-v3a",
|
||||||
ERRATA_MIDR_RANGE_LIST(ca57_a72),
|
.capability = ARM64_SPECTRE_V3A,
|
||||||
|
.type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
|
||||||
|
.matches = has_spectre_v3a,
|
||||||
|
.cpu_enable = spectre_v3a_enable_mitigation,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
/*
|
/*
|
||||||
* Handle detection, reporting and mitigation of Spectre v1, v2 and v4, as
|
* Handle detection, reporting and mitigation of Spectre v1, v2, v3a and v4, as
|
||||||
* detailed at:
|
* detailed at:
|
||||||
*
|
*
|
||||||
* https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability
|
* https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include <asm/spectre.h>
|
#include <asm/spectre.h>
|
||||||
#include <asm/traps.h>
|
#include <asm/traps.h>
|
||||||
|
#include <asm/virt.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We try to ensure that the mitigation state can never change as the result of
|
* We try to ensure that the mitigation state can never change as the result of
|
||||||
|
@ -170,72 +171,26 @@ bool has_spectre_v2(const struct arm64_cpu_capabilities *entry, int scope)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data);
|
|
||||||
|
|
||||||
enum mitigation_state arm64_get_spectre_v2_state(void)
|
enum mitigation_state arm64_get_spectre_v2_state(void)
|
||||||
{
|
{
|
||||||
return spectre_v2_state;
|
return spectre_v2_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KVM
|
DEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data);
|
||||||
#include <asm/cacheflush.h>
|
|
||||||
#include <asm/kvm_asm.h>
|
|
||||||
|
|
||||||
atomic_t arm64_el2_vector_last_slot = ATOMIC_INIT(-1);
|
|
||||||
|
|
||||||
static void __copy_hyp_vect_bpi(int slot, const char *hyp_vecs_start,
|
|
||||||
const char *hyp_vecs_end)
|
|
||||||
{
|
|
||||||
void *dst = lm_alias(__bp_harden_hyp_vecs + slot * SZ_2K);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < SZ_2K; i += 0x80)
|
|
||||||
memcpy(dst + i, hyp_vecs_start, hyp_vecs_end - hyp_vecs_start);
|
|
||||||
|
|
||||||
__flush_icache_range((uintptr_t)dst, (uintptr_t)dst + SZ_2K);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void install_bp_hardening_cb(bp_hardening_cb_t fn)
|
static void install_bp_hardening_cb(bp_hardening_cb_t fn)
|
||||||
{
|
{
|
||||||
static DEFINE_RAW_SPINLOCK(bp_lock);
|
__this_cpu_write(bp_hardening_data.fn, fn);
|
||||||
int cpu, slot = -1;
|
|
||||||
const char *hyp_vecs_start = __smccc_workaround_1_smc;
|
|
||||||
const char *hyp_vecs_end = __smccc_workaround_1_smc +
|
|
||||||
__SMCCC_WORKAROUND_1_SMC_SZ;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Vinz Clortho takes the hyp_vecs start/end "keys" at
|
* Vinz Clortho takes the hyp_vecs start/end "keys" at
|
||||||
* the door when we're a guest. Skip the hyp-vectors work.
|
* the door when we're a guest. Skip the hyp-vectors work.
|
||||||
*/
|
*/
|
||||||
if (!is_hyp_mode_available()) {
|
if (!is_hyp_mode_available())
|
||||||
__this_cpu_write(bp_hardening_data.fn, fn);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
raw_spin_lock(&bp_lock);
|
__this_cpu_write(bp_hardening_data.slot, HYP_VECTOR_SPECTRE_DIRECT);
|
||||||
for_each_possible_cpu(cpu) {
|
|
||||||
if (per_cpu(bp_hardening_data.fn, cpu) == fn) {
|
|
||||||
slot = per_cpu(bp_hardening_data.hyp_vectors_slot, cpu);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot == -1) {
|
|
||||||
slot = atomic_inc_return(&arm64_el2_vector_last_slot);
|
|
||||||
BUG_ON(slot >= BP_HARDEN_EL2_SLOTS);
|
|
||||||
__copy_hyp_vect_bpi(slot, hyp_vecs_start, hyp_vecs_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
__this_cpu_write(bp_hardening_data.hyp_vectors_slot, slot);
|
|
||||||
__this_cpu_write(bp_hardening_data.fn, fn);
|
|
||||||
raw_spin_unlock(&bp_lock);
|
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
static void install_bp_hardening_cb(bp_hardening_cb_t fn)
|
|
||||||
{
|
|
||||||
__this_cpu_write(bp_hardening_data.fn, fn);
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_KVM */
|
|
||||||
|
|
||||||
static void call_smc_arch_workaround_1(void)
|
static void call_smc_arch_workaround_1(void)
|
||||||
{
|
{
|
||||||
|
@ -316,6 +271,33 @@ void spectre_v2_enable_mitigation(const struct arm64_cpu_capabilities *__unused)
|
||||||
update_mitigation_state(&spectre_v2_state, state);
|
update_mitigation_state(&spectre_v2_state, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Spectre-v3a.
|
||||||
|
*
|
||||||
|
* Phew, there's not an awful lot to do here! We just instruct EL2 to use
|
||||||
|
* an indirect trampoline for the hyp vectors so that guests can't read
|
||||||
|
* VBAR_EL2 to defeat randomisation of the hypervisor VA layout.
|
||||||
|
*/
|
||||||
|
bool has_spectre_v3a(const struct arm64_cpu_capabilities *entry, int scope)
|
||||||
|
{
|
||||||
|
static const struct midr_range spectre_v3a_unsafe_list[] = {
|
||||||
|
MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
|
||||||
|
MIDR_ALL_VERSIONS(MIDR_CORTEX_A72),
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
|
||||||
|
return is_midr_in_range_list(read_cpuid_id(), spectre_v3a_unsafe_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spectre_v3a_enable_mitigation(const struct arm64_cpu_capabilities *__unused)
|
||||||
|
{
|
||||||
|
struct bp_hardening_data *data = this_cpu_ptr(&bp_hardening_data);
|
||||||
|
|
||||||
|
if (this_cpu_has_cap(ARM64_SPECTRE_V3A))
|
||||||
|
data->slot += HYP_VECTOR_INDIRECT;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Spectre v4.
|
* Spectre v4.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1310,37 +1310,44 @@ static unsigned long nvhe_percpu_order(void)
|
||||||
return size ? get_order(size) : 0;
|
return size ? get_order(size) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_map_vectors(void)
|
/* A lookup table holding the hypervisor VA for each vector slot */
|
||||||
|
static void *hyp_spectre_vector_selector[BP_HARDEN_EL2_SLOTS];
|
||||||
|
|
||||||
|
static int __kvm_vector_slot2idx(enum arm64_hyp_spectre_vector slot)
|
||||||
{
|
{
|
||||||
/*
|
return slot - (slot != HYP_VECTOR_DIRECT);
|
||||||
* SV2 = ARM64_SPECTRE_V2
|
}
|
||||||
* HEL2 = ARM64_HARDEN_EL2_VECTORS
|
|
||||||
*
|
static void kvm_init_vector_slot(void *base, enum arm64_hyp_spectre_vector slot)
|
||||||
* !SV2 + !HEL2 -> use direct vectors
|
{
|
||||||
* SV2 + !HEL2 -> use hardened vectors in place
|
int idx = __kvm_vector_slot2idx(slot);
|
||||||
* !SV2 + HEL2 -> allocate one vector slot and use exec mapping
|
|
||||||
* SV2 + HEL2 -> use hardened vectors and use exec mapping
|
hyp_spectre_vector_selector[slot] = base + (idx * SZ_2K);
|
||||||
*/
|
}
|
||||||
if (cpus_have_const_cap(ARM64_SPECTRE_V2)) {
|
|
||||||
__kvm_bp_vect_base = kvm_ksym_ref(__bp_harden_hyp_vecs);
|
static int kvm_init_vector_slots(void)
|
||||||
__kvm_bp_vect_base = kern_hyp_va(__kvm_bp_vect_base);
|
{
|
||||||
}
|
int err;
|
||||||
|
void *base;
|
||||||
if (cpus_have_const_cap(ARM64_HARDEN_EL2_VECTORS)) {
|
|
||||||
phys_addr_t vect_pa = __pa_symbol(__bp_harden_hyp_vecs);
|
base = kern_hyp_va(kvm_ksym_ref(__kvm_hyp_vector));
|
||||||
unsigned long size = __BP_HARDEN_HYP_VECS_SZ;
|
kvm_init_vector_slot(base, HYP_VECTOR_DIRECT);
|
||||||
|
|
||||||
/*
|
base = kern_hyp_va(kvm_ksym_ref(__bp_harden_hyp_vecs));
|
||||||
* Always allocate a spare vector slot, as we don't
|
kvm_init_vector_slot(base, HYP_VECTOR_SPECTRE_DIRECT);
|
||||||
* know yet which CPUs have a BP hardening slot that
|
|
||||||
* we can reuse.
|
if (!cpus_have_const_cap(ARM64_SPECTRE_V3A))
|
||||||
*/
|
return 0;
|
||||||
__kvm_harden_el2_vector_slot = atomic_inc_return(&arm64_el2_vector_last_slot);
|
|
||||||
BUG_ON(__kvm_harden_el2_vector_slot >= BP_HARDEN_EL2_SLOTS);
|
if (!has_vhe()) {
|
||||||
return create_hyp_exec_mappings(vect_pa, size,
|
err = create_hyp_exec_mappings(__pa_symbol(__bp_harden_hyp_vecs),
|
||||||
&__kvm_bp_vect_base);
|
__BP_HARDEN_HYP_VECS_SZ, &base);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kvm_init_vector_slot(base, HYP_VECTOR_INDIRECT);
|
||||||
|
kvm_init_vector_slot(base, HYP_VECTOR_SPECTRE_INDIRECT);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1395,13 +1402,40 @@ static void cpu_hyp_reset(void)
|
||||||
__hyp_reset_vectors();
|
__hyp_reset_vectors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EL2 vectors can be mapped and rerouted in a number of ways,
|
||||||
|
* depending on the kernel configuration and CPU present:
|
||||||
|
*
|
||||||
|
* - If the CPU is affected by Spectre-v2, the hardening sequence is
|
||||||
|
* placed in one of the vector slots, which is executed before jumping
|
||||||
|
* to the real vectors.
|
||||||
|
*
|
||||||
|
* - If the CPU also has the ARM64_SPECTRE_V3A cap, the slot
|
||||||
|
* containing the hardening sequence is mapped next to the idmap page,
|
||||||
|
* and executed before jumping to the real vectors.
|
||||||
|
*
|
||||||
|
* - If the CPU only has the ARM64_SPECTRE_V3A cap, then an
|
||||||
|
* empty slot is selected, mapped next to the idmap page, and
|
||||||
|
* executed before jumping to the real vectors.
|
||||||
|
*
|
||||||
|
* Note that ARM64_SPECTRE_V3A is somewhat incompatible with
|
||||||
|
* VHE, as we don't have hypervisor-specific mappings. If the system
|
||||||
|
* is VHE and yet selects this capability, it will be ignored.
|
||||||
|
*/
|
||||||
|
static void cpu_set_hyp_vector(void)
|
||||||
|
{
|
||||||
|
struct bp_hardening_data *data = this_cpu_ptr(&bp_hardening_data);
|
||||||
|
void *vector = hyp_spectre_vector_selector[data->slot];
|
||||||
|
|
||||||
|
*this_cpu_ptr_hyp_sym(kvm_hyp_vector) = (unsigned long)vector;
|
||||||
|
}
|
||||||
|
|
||||||
static void cpu_hyp_reinit(void)
|
static void cpu_hyp_reinit(void)
|
||||||
{
|
{
|
||||||
kvm_init_host_cpu_context(&this_cpu_ptr_hyp_sym(kvm_host_data)->host_ctxt);
|
kvm_init_host_cpu_context(&this_cpu_ptr_hyp_sym(kvm_host_data)->host_ctxt);
|
||||||
|
|
||||||
cpu_hyp_reset();
|
cpu_hyp_reset();
|
||||||
|
cpu_set_hyp_vector();
|
||||||
*this_cpu_ptr_hyp_sym(kvm_hyp_vector) = (unsigned long)kvm_get_hyp_vector();
|
|
||||||
|
|
||||||
if (is_kernel_in_hyp_mode())
|
if (is_kernel_in_hyp_mode())
|
||||||
kvm_timer_init_vhe();
|
kvm_timer_init_vhe();
|
||||||
|
@ -1631,12 +1665,6 @@ static int init_hyp_mode(void)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = kvm_map_vectors();
|
|
||||||
if (err) {
|
|
||||||
kvm_err("Cannot map vectors\n");
|
|
||||||
goto out_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Map the Hyp stack pages
|
* Map the Hyp stack pages
|
||||||
*/
|
*/
|
||||||
|
@ -1780,6 +1808,12 @@ int kvm_arch_init(void *opaque)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = kvm_init_vector_slots();
|
||||||
|
if (err) {
|
||||||
|
kvm_err("Cannot initialise vector slots\n");
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
|
|
||||||
err = init_subsystems();
|
err = init_subsystems();
|
||||||
if (err)
|
if (err)
|
||||||
goto out_hyp;
|
goto out_hyp;
|
||||||
|
|
|
@ -10,4 +10,4 @@ subdir-ccflags-y := -I$(incdir) \
|
||||||
-DDISABLE_BRANCH_PROFILING \
|
-DDISABLE_BRANCH_PROFILING \
|
||||||
$(DISABLE_STACKLEAK_PLUGIN)
|
$(DISABLE_STACKLEAK_PLUGIN)
|
||||||
|
|
||||||
obj-$(CONFIG_KVM) += vhe/ nvhe/ pgtable.o smccc_wa.o
|
obj-$(CONFIG_KVM) += vhe/ nvhe/ pgtable.o
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <asm/kvm_arm.h>
|
#include <asm/kvm_arm.h>
|
||||||
#include <asm/kvm_asm.h>
|
#include <asm/kvm_asm.h>
|
||||||
#include <asm/mmu.h>
|
#include <asm/mmu.h>
|
||||||
|
#include <asm/spectre.h>
|
||||||
|
|
||||||
.macro save_caller_saved_regs_vect
|
.macro save_caller_saved_regs_vect
|
||||||
/* x0 and x1 were saved in the vector entry */
|
/* x0 and x1 were saved in the vector entry */
|
||||||
|
@ -187,52 +188,60 @@ SYM_CODE_START(__kvm_hyp_vector)
|
||||||
valid_vect el1_error // Error 32-bit EL1
|
valid_vect el1_error // Error 32-bit EL1
|
||||||
SYM_CODE_END(__kvm_hyp_vector)
|
SYM_CODE_END(__kvm_hyp_vector)
|
||||||
|
|
||||||
.macro hyp_ventry
|
.macro spectrev2_smccc_wa1_smc
|
||||||
.align 7
|
sub sp, sp, #(8 * 4)
|
||||||
1: esb
|
stp x2, x3, [sp, #(8 * 0)]
|
||||||
.rept 26
|
stp x0, x1, [sp, #(8 * 2)]
|
||||||
nop
|
mov w0, #ARM_SMCCC_ARCH_WORKAROUND_1
|
||||||
.endr
|
smc #0
|
||||||
/*
|
ldp x2, x3, [sp, #(8 * 0)]
|
||||||
* The default sequence is to directly branch to the KVM vectors,
|
add sp, sp, #(8 * 2)
|
||||||
* using the computed offset. This applies for VHE as well as
|
|
||||||
* !ARM64_HARDEN_EL2_VECTORS. The first vector must always run the preamble.
|
|
||||||
*
|
|
||||||
* For ARM64_HARDEN_EL2_VECTORS configurations, this gets replaced
|
|
||||||
* with:
|
|
||||||
*
|
|
||||||
* stp x0, x1, [sp, #-16]!
|
|
||||||
* movz x0, #(addr & 0xffff)
|
|
||||||
* movk x0, #((addr >> 16) & 0xffff), lsl #16
|
|
||||||
* movk x0, #((addr >> 32) & 0xffff), lsl #32
|
|
||||||
* br x0
|
|
||||||
*
|
|
||||||
* Where:
|
|
||||||
* addr = kern_hyp_va(__kvm_hyp_vector) + vector-offset + KVM_VECTOR_PREAMBLE.
|
|
||||||
* See kvm_patch_vector_branch for details.
|
|
||||||
*/
|
|
||||||
alternative_cb kvm_patch_vector_branch
|
|
||||||
stp x0, x1, [sp, #-16]!
|
|
||||||
b __kvm_hyp_vector + (1b - 0b + KVM_VECTOR_PREAMBLE)
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
alternative_cb_end
|
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro generate_vectors
|
.macro hyp_ventry indirect, spectrev2
|
||||||
|
.align 7
|
||||||
|
1: esb
|
||||||
|
.if \spectrev2 != 0
|
||||||
|
spectrev2_smccc_wa1_smc
|
||||||
|
.else
|
||||||
|
stp x0, x1, [sp, #-16]!
|
||||||
|
.endif
|
||||||
|
.if \indirect != 0
|
||||||
|
alternative_cb kvm_patch_vector_branch
|
||||||
|
/*
|
||||||
|
* For ARM64_SPECTRE_V3A configurations, these NOPs get replaced with:
|
||||||
|
*
|
||||||
|
* movz x0, #(addr & 0xffff)
|
||||||
|
* movk x0, #((addr >> 16) & 0xffff), lsl #16
|
||||||
|
* movk x0, #((addr >> 32) & 0xffff), lsl #32
|
||||||
|
* br x0
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
* addr = kern_hyp_va(__kvm_hyp_vector) + vector-offset + KVM_VECTOR_PREAMBLE.
|
||||||
|
* See kvm_patch_vector_branch for details.
|
||||||
|
*/
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
alternative_cb_end
|
||||||
|
.endif
|
||||||
|
b __kvm_hyp_vector + (1b - 0b + KVM_VECTOR_PREAMBLE)
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro generate_vectors indirect, spectrev2
|
||||||
0:
|
0:
|
||||||
.rept 16
|
.rept 16
|
||||||
hyp_ventry
|
hyp_ventry \indirect, \spectrev2
|
||||||
.endr
|
.endr
|
||||||
.org 0b + SZ_2K // Safety measure
|
.org 0b + SZ_2K // Safety measure
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.align 11
|
.align 11
|
||||||
SYM_CODE_START(__bp_harden_hyp_vecs)
|
SYM_CODE_START(__bp_harden_hyp_vecs)
|
||||||
.rept BP_HARDEN_EL2_SLOTS
|
generate_vectors indirect = 0, spectrev2 = 1 // HYP_VECTOR_SPECTRE_DIRECT
|
||||||
generate_vectors
|
generate_vectors indirect = 1, spectrev2 = 0 // HYP_VECTOR_INDIRECT
|
||||||
.endr
|
generate_vectors indirect = 1, spectrev2 = 1 // HYP_VECTOR_SPECTRE_INDIRECT
|
||||||
1: .org __bp_harden_hyp_vecs + __BP_HARDEN_HYP_VECS_SZ
|
1: .org __bp_harden_hyp_vecs + __BP_HARDEN_HYP_VECS_SZ
|
||||||
.org 1b
|
.org 1b
|
||||||
SYM_CODE_END(__bp_harden_hyp_vecs)
|
SYM_CODE_END(__bp_harden_hyp_vecs)
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2015-2018 - ARM Ltd
|
|
||||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/arm-smccc.h>
|
|
||||||
#include <linux/linkage.h>
|
|
||||||
|
|
||||||
#include <asm/kvm_asm.h>
|
|
||||||
#include <asm/kvm_mmu.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is not executed directly and is instead copied into the vectors
|
|
||||||
* by install_bp_hardening_cb().
|
|
||||||
*/
|
|
||||||
.data
|
|
||||||
.pushsection .rodata
|
|
||||||
.global __smccc_workaround_1_smc
|
|
||||||
SYM_DATA_START(__smccc_workaround_1_smc)
|
|
||||||
esb
|
|
||||||
sub sp, sp, #(8 * 4)
|
|
||||||
stp x2, x3, [sp, #(8 * 0)]
|
|
||||||
stp x0, x1, [sp, #(8 * 2)]
|
|
||||||
mov w0, #ARM_SMCCC_ARCH_WORKAROUND_1
|
|
||||||
smc #0
|
|
||||||
ldp x2, x3, [sp, #(8 * 0)]
|
|
||||||
ldp x0, x1, [sp, #(8 * 2)]
|
|
||||||
add sp, sp, #(8 * 4)
|
|
||||||
1: .org __smccc_workaround_1_smc + __SMCCC_WORKAROUND_1_SMC_SZ
|
|
||||||
.org 1b
|
|
||||||
SYM_DATA_END(__smccc_workaround_1_smc)
|
|
|
@ -132,21 +132,16 @@ void __init kvm_update_va_mask(struct alt_instr *alt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *__kvm_bp_vect_base;
|
|
||||||
int __kvm_harden_el2_vector_slot;
|
|
||||||
|
|
||||||
void kvm_patch_vector_branch(struct alt_instr *alt,
|
void kvm_patch_vector_branch(struct alt_instr *alt,
|
||||||
__le32 *origptr, __le32 *updptr, int nr_inst)
|
__le32 *origptr, __le32 *updptr, int nr_inst)
|
||||||
{
|
{
|
||||||
u64 addr;
|
u64 addr;
|
||||||
u32 insn;
|
u32 insn;
|
||||||
|
|
||||||
BUG_ON(nr_inst != 5);
|
BUG_ON(nr_inst != 4);
|
||||||
|
|
||||||
if (has_vhe() || !cpus_have_const_cap(ARM64_HARDEN_EL2_VECTORS)) {
|
if (!cpus_have_const_cap(ARM64_SPECTRE_V3A) || WARN_ON_ONCE(has_vhe()))
|
||||||
WARN_ON_ONCE(cpus_have_const_cap(ARM64_HARDEN_EL2_VECTORS));
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute HYP VA by using the same computation as kern_hyp_va()
|
* Compute HYP VA by using the same computation as kern_hyp_va()
|
||||||
|
@ -164,15 +159,6 @@ void kvm_patch_vector_branch(struct alt_instr *alt,
|
||||||
*/
|
*/
|
||||||
addr += KVM_VECTOR_PREAMBLE;
|
addr += KVM_VECTOR_PREAMBLE;
|
||||||
|
|
||||||
/* stp x0, x1, [sp, #-16]! */
|
|
||||||
insn = aarch64_insn_gen_load_store_pair(AARCH64_INSN_REG_0,
|
|
||||||
AARCH64_INSN_REG_1,
|
|
||||||
AARCH64_INSN_REG_SP,
|
|
||||||
-16,
|
|
||||||
AARCH64_INSN_VARIANT_64BIT,
|
|
||||||
AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX);
|
|
||||||
*updptr++ = cpu_to_le32(insn);
|
|
||||||
|
|
||||||
/* movz x0, #(addr & 0xffff) */
|
/* movz x0, #(addr & 0xffff) */
|
||||||
insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0,
|
insn = aarch64_insn_gen_movewide(AARCH64_INSN_REG_0,
|
||||||
(u16)addr,
|
(u16)addr,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue