Commit 92cdeac6 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge tag 'kvm-x86-svm-7.1' of https://github.com/kvm-x86/linux into HEAD

KVM SVM changes for 7.1

 - Fix and optimize IRQ window inhibit handling for AVIC (the tracking needs to
   be per-vCPU, e.g. so that KVM doesn't prematurely re-enable AVIC if multiple
   vCPUs have to-be-injected IRQs).

 - Fix an undefined behavior warning where a crafty userspace can read the
   "avic" module param before it's fully initialized.

 - Fix a (likely benign) bug in the "OS-visible workarounds" handling, where
   KVM could clobber state when enabling virtualization on multiple CPUs in
   parallel, and clean up and optimize the code.

 - Drop a WARN in KVM_MEMORY_ENCRYPT_REG_REGION where KVM complains about a
   "too large" size based purely on user input, and clean up and harden the
   related pinning code.

 - Disallow synchronizing a VMSA of an already-launched/encrypted vCPU, as
   doing so for an SNP guest will trigger an RMP violation #PF and crash the
   host.

 - Protect all of sev_mem_enc_register_region() with kvm->lock to ensure
   sev_guest() is stable for the entire of the function.

 - Lock all vCPUs when synchronizing VMSAs for SNP guests to ensure the VMSA
   page isn't actively being used.

 - Overhaul KVM's APIs for detecting SEV+ guests so that VM-scoped queries are
   required to hold kvm->lock (KVM has had multiple bugs due "is SEV?" checks
   becoming stale), enforced by lockdep.  Add and use vCPU-scoped APIs when
   possible/appropriate, as all checks that originate from a vCPU are
   guaranteed to be stable.

 - Convert a pile of kvm->lock SEV code to guard().
parents 4a530993 bc0932cf
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -1449,6 +1449,7 @@ struct kvm_arch {
	struct kvm_pit *vpit;
#endif
	atomic_t vapics_in_nmi_mode;

	struct mutex apic_map_lock;
	struct kvm_apic_map __rcu *apic_map;
	atomic_t apic_map_dirty;
@@ -1456,8 +1457,22 @@ struct kvm_arch {
	bool apic_access_memslot_enabled;
	bool apic_access_memslot_inhibited;

	/* Protects apicv_inhibit_reasons */
	/*
	 * Force apicv_update_lock and apicv_nr_irq_window_req to reside in a
	 * dedicated cacheline.  They are write-mostly, whereas most everything
	 * else in kvm_arch is read-mostly.  Note that apicv_inhibit_reasons is
	 * read-mostly: toggling VM-wide inhibits is rare; _checking_ for
	 * inhibits is common.
	 */
	____cacheline_aligned
	/*
	 * Protects apicv_inhibit_reasons and apicv_nr_irq_window_req (with an
	 * asterisk, see kvm_inc_or_dec_irq_window_inhibit() for details).
	 */
	struct rw_semaphore apicv_update_lock;
	atomic_t apicv_nr_irq_window_req;
	____cacheline_aligned

	unsigned long apicv_inhibit_reasons;

	gpa_t wall_clock;
@@ -2329,6 +2344,18 @@ static inline void kvm_clear_apicv_inhibit(struct kvm *kvm,
	kvm_set_or_clear_apicv_inhibit(kvm, reason, false);
}

void kvm_inc_or_dec_irq_window_inhibit(struct kvm *kvm, bool inc);

static inline void kvm_inc_apicv_irq_window_req(struct kvm *kvm)
{
	kvm_inc_or_dec_irq_window_inhibit(kvm, true);
}

static inline void kvm_dec_apicv_irq_window_req(struct kvm *kvm)
{
	kvm_inc_or_dec_irq_window_inhibit(kvm, false);
}

int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code,
		       void *insn, int insn_len);
void kvm_mmu_print_sptes(struct kvm_vcpu *vcpu, gpa_t gpa, const char *msg);
+13 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/amd-iommu.h>
#include <linux/kvm_host.h>
#include <linux/kvm_irqfd.h>
#include <linux/sysfs.h>

#include <asm/irq_remapping.h>
#include <asm/msr.h>
@@ -76,10 +77,20 @@ static int avic_param_set(const char *val, const struct kernel_param *kp)
	return param_set_bint(val, kp);
}

static int avic_param_get(char *buffer, const struct kernel_param *kp)
{
	int val = *(int *)kp->arg;

	if (val == AVIC_AUTO_MODE)
		return sysfs_emit(buffer, "N\n");

	return param_get_bool(buffer, kp);
}

static const struct kernel_param_ops avic_ops = {
	.flags = KERNEL_PARAM_OPS_FL_NOARG,
	.set = avic_param_set,
	.get = param_get_bool,
	.get = avic_param_get,
};

/*
@@ -226,7 +237,7 @@ static void avic_deactivate_vmcb(struct vcpu_svm *svm)
	vmcb->control.int_ctl &= ~(AVIC_ENABLE_MASK | X2APIC_MODE_MASK);
	vmcb->control.avic_physical_id &= ~AVIC_PHYSICAL_MAX_INDEX_MASK;

	if (!sev_es_guest(svm->vcpu.kvm))
	if (!is_sev_es_guest(&svm->vcpu))
		svm_set_intercept(svm, INTERCEPT_CR8_WRITE);

	/*
+211 −155

File changed.

Preview size limit exceeded, changes collapsed.

+134 −103

File changed.

Preview size limit exceeded, changes collapsed.

+29 −8
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ enum {
/* TPR and CR2 are always written before VMRUN */
#define VMCB_ALWAYS_DIRTY_MASK	((1U << VMCB_INTR) | (1U << VMCB_CR2))

#ifdef CONFIG_KVM_AMD_SEV
struct kvm_sev_info {
	bool active;		/* SEV enabled guest */
	bool es_active;		/* SEV-ES enabled guest */
@@ -117,6 +118,7 @@ struct kvm_sev_info {
	cpumask_var_t have_run_cpus; /* CPUs that have done VMRUN for this VM. */
	bool snp_certs_enabled;	/* SNP certificate-fetching support. */
};
#endif

struct kvm_svm {
	struct kvm kvm;
@@ -127,7 +129,9 @@ struct kvm_svm {
	u64 *avic_physical_id_table;
	struct hlist_node hnode;

#ifdef CONFIG_KVM_AMD_SEV
	struct kvm_sev_info sev_info;
#endif
};

struct kvm_vcpu;
@@ -349,6 +353,7 @@ struct vcpu_svm {

	bool guest_state_loaded;

	bool avic_irq_window;
	bool x2avic_msrs_intercepted;
	bool lbr_msrs_intercepted;

@@ -378,34 +383,48 @@ static __always_inline struct kvm_svm *to_kvm_svm(struct kvm *kvm)
	return container_of(kvm, struct kvm_svm, kvm);
}

#ifdef CONFIG_KVM_AMD_SEV
static __always_inline struct kvm_sev_info *to_kvm_sev_info(struct kvm *kvm)
{
	return &to_kvm_svm(kvm)->sev_info;
}

#ifdef CONFIG_KVM_AMD_SEV
static __always_inline bool sev_guest(struct kvm *kvm)
static __always_inline bool ____sev_guest(struct kvm *kvm)
{
	return to_kvm_sev_info(kvm)->active;
}
static __always_inline bool sev_es_guest(struct kvm *kvm)
static __always_inline bool ____sev_es_guest(struct kvm *kvm)
{
	struct kvm_sev_info *sev = to_kvm_sev_info(kvm);

	return sev->es_active && !WARN_ON_ONCE(!sev->active);
}

static __always_inline bool sev_snp_guest(struct kvm *kvm)
static __always_inline bool ____sev_snp_guest(struct kvm *kvm)
{
	struct kvm_sev_info *sev = to_kvm_sev_info(kvm);

	return (sev->vmsa_features & SVM_SEV_FEAT_SNP_ACTIVE) &&
	       !WARN_ON_ONCE(!sev_es_guest(kvm));
	       !WARN_ON_ONCE(!____sev_es_guest(kvm));
}

static __always_inline bool is_sev_guest(struct kvm_vcpu *vcpu)
{
	return ____sev_guest(vcpu->kvm);
}
static __always_inline bool is_sev_es_guest(struct kvm_vcpu *vcpu)
{
	return ____sev_es_guest(vcpu->kvm);
}

static __always_inline bool is_sev_snp_guest(struct kvm_vcpu *vcpu)
{
	return ____sev_snp_guest(vcpu->kvm);
}
#else
#define sev_guest(kvm) false
#define sev_es_guest(kvm) false
#define sev_snp_guest(kvm) false
#define is_sev_guest(vcpu) false
#define is_sev_es_guest(vcpu) false
#define is_sev_snp_guest(vcpu) false
#endif

static inline bool ghcb_gpa_is_registered(struct vcpu_svm *svm, u64 val)
@@ -923,6 +942,7 @@ static inline struct page *snp_safe_alloc_page(void)

int sev_vcpu_create(struct kvm_vcpu *vcpu);
void sev_free_vcpu(struct kvm_vcpu *vcpu);
void sev_vm_init(struct kvm *kvm);
void sev_vm_destroy(struct kvm *kvm);
void __init sev_set_cpu_caps(void);
void __init sev_hardware_setup(void);
@@ -949,6 +969,7 @@ static inline struct page *snp_safe_alloc_page(void)

static inline int sev_vcpu_create(struct kvm_vcpu *vcpu) { return 0; }
static inline void sev_free_vcpu(struct kvm_vcpu *vcpu) {}
static inline void sev_vm_init(struct kvm *kvm) {}
static inline void sev_vm_destroy(struct kvm *kvm) {}
static inline void __init sev_set_cpu_caps(void) {}
static inline void __init sev_hardware_setup(void) {}
Loading