Commit 66d5b53e authored by Fuad Tabba's avatar Fuad Tabba Committed by Marc Zyngier
Browse files

KVM: arm64: Allocate memory mapped at hyp for host sve state in pKVM



Protected mode needs to maintain (save/restore) the host's sve
state, rather than relying on the host kernel to do that. This is
to avoid leaking information to the host about guests and the
type of operations they are performing.

As a first step towards that, allocate memory mapped at hyp, per
cpu, for the host sve state. The following patch will use this
memory to save/restore the host state.

Reviewed-by: default avatarOliver Upton <oliver.upton@linux.dev>
Signed-off-by: default avatarFuad Tabba <tabba@google.com>
Link: https://lore.kernel.org/r/20240603122852.3923848-6-tabba@google.com


Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent e511e08a
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ static inline enum kvm_mode kvm_get_mode(void) { return KVM_MODE_NONE; };
DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);

extern unsigned int __ro_after_init kvm_sve_max_vl;
extern unsigned int __ro_after_init kvm_host_sve_max_vl;
int __init kvm_arm_init_sve(void);

u32 __attribute_const__ kvm_target_cpu(void);
@@ -521,6 +522,20 @@ struct kvm_cpu_context {
	u64 *vncr_array;
};

struct cpu_sve_state {
	__u64 zcr_el1;

	/*
	 * Ordering is important since __sve_save_state/__sve_restore_state
	 * relies on it.
	 */
	__u32 fpsr;
	__u32 fpcr;

	/* Must be SVE_VQ_BYTES (128 bit) aligned. */
	__u8 sve_regs[];
};

/*
 * This structure is instantiated on a per-CPU basis, and contains
 * data that is:
@@ -534,7 +549,9 @@ struct kvm_cpu_context {
 */
struct kvm_host_data {
	struct kvm_cpu_context host_ctxt;

	struct user_fpsimd_state *fpsimd_state;	/* hyp VA */
	struct cpu_sve_state *sve_state;	/* hyp VA */

	/* Ownership of the FP regs */
	enum {
+1 −0
Original line number Diff line number Diff line
@@ -143,5 +143,6 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val);

extern unsigned long kvm_nvhe_sym(__icache_flags);
extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl);

#endif /* __ARM64_KVM_HYP_H__ */
+9 −0
Original line number Diff line number Diff line
@@ -128,4 +128,13 @@ static inline unsigned long hyp_ffa_proxy_pages(void)
	return (2 * KVM_FFA_MBOX_NR_PAGES) + DIV_ROUND_UP(desc_max, PAGE_SIZE);
}

static inline size_t pkvm_host_sve_state_size(void)
{
	if (!system_supports_sve())
		return 0;

	return size_add(sizeof(struct cpu_sve_state),
			SVE_SIG_REGS_SIZE(sve_vq_from_vl(kvm_host_sve_max_vl)));
}

#endif	/* __ARM64_KVM_PKVM_H__ */
+68 −0
Original line number Diff line number Diff line
@@ -1931,6 +1931,11 @@ static unsigned long nvhe_percpu_order(void)
	return size ? get_order(size) : 0;
}

static size_t pkvm_host_sve_state_order(void)
{
	return get_order(pkvm_host_sve_state_size());
}

/* A lookup table holding the hypervisor VA for each vector slot */
static void *hyp_spectre_vector_selector[BP_HARDEN_EL2_SLOTS];

@@ -2310,12 +2315,20 @@ static void __init teardown_subsystems(void)

static void __init teardown_hyp_mode(void)
{
	bool free_sve = system_supports_sve() && is_protected_kvm_enabled();
	int cpu;

	free_hyp_pgds();
	for_each_possible_cpu(cpu) {
		free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));
		free_pages(kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu], nvhe_percpu_order());

		if (free_sve) {
			struct cpu_sve_state *sve_state;

			sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state;
			free_pages((unsigned long) sve_state, pkvm_host_sve_state_order());
		}
	}
}

@@ -2398,6 +2411,50 @@ static int __init kvm_hyp_init_protection(u32 hyp_va_bits)
	return 0;
}

static int init_pkvm_host_sve_state(void)
{
	int cpu;

	if (!system_supports_sve())
		return 0;

	/* Allocate pages for host sve state in protected mode. */
	for_each_possible_cpu(cpu) {
		struct page *page = alloc_pages(GFP_KERNEL, pkvm_host_sve_state_order());

		if (!page)
			return -ENOMEM;

		per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = page_address(page);
	}

	/*
	 * Don't map the pages in hyp since these are only used in protected
	 * mode, which will (re)create its own mapping when initialized.
	 */

	return 0;
}

/*
 * Finalizes the initialization of hyp mode, once everything else is initialized
 * and the initialziation process cannot fail.
 */
static void finalize_init_hyp_mode(void)
{
	int cpu;

	if (!is_protected_kvm_enabled() || !system_supports_sve())
		return;

	for_each_possible_cpu(cpu) {
		struct cpu_sve_state *sve_state;

		sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state;
		per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = kern_hyp_va(sve_state);
	}
}

static void pkvm_hyp_init_ptrauth(void)
{
	struct kvm_cpu_context *hyp_ctxt;
@@ -2566,6 +2623,10 @@ static int __init init_hyp_mode(void)
			goto out_err;
		}

		err = init_pkvm_host_sve_state();
		if (err)
			goto out_err;

		err = kvm_hyp_init_protection(hyp_va_bits);
		if (err) {
			kvm_err("Failed to init hyp memory protection\n");
@@ -2730,6 +2791,13 @@ static __init int kvm_arm_init(void)
	if (err)
		goto out_subs;

	/*
	 * This should be called after initialization is done and failure isn't
	 * possible anymore.
	 */
	if (!in_hyp_mode)
		finalize_init_hyp_mode();

	kvm_arm_initialised = true;

	return 0;
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ unsigned long __icache_flags;
/* Used by kvm_get_vttbr(). */
unsigned int kvm_arm_vmid_bits;

unsigned int kvm_host_sve_max_vl;

/*
 * Set trap register values based on features in ID_AA64PFR0.
 */
Loading