@@ -1367,6 +1367,28 @@ static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
return ret;
}
+enum es_result sev_notify_savic_gpa(u64 gpa)
+{
+ struct ghcb_state state;
+ struct es_em_ctxt ctxt;
+ unsigned long flags;
+ struct ghcb *ghcb;
+ int ret = 0;
+
+ local_irq_save(flags);
+
+ ghcb = __sev_get_ghcb(&state);
+
+ vc_ghcb_invalidate(ghcb);
+
+ ret = sev_es_ghcb_hv_call(ghcb, &ctxt, SVM_VMGEXIT_SECURE_AVIC_GPA, gpa, 0);
+
+ __sev_put_ghcb(&state);
+
+ local_irq_restore(flags);
+ return ret;
+}
+
static void snp_register_per_cpu_ghcb(void)
{
struct sev_es_runtime_data *data;
@@ -302,6 +302,7 @@ struct apic {
/* Probe, setup and smpboot functions */
int (*probe)(void);
+ void (*setup)(void);
int (*acpi_madt_oem_check)(char *oem_id, char *oem_table_id);
void (*init_apic_ldr)(void);
@@ -399,6 +399,7 @@ u64 snp_get_unsupported_features(u64 status);
u64 sev_get_status(void);
void sev_show_status(void);
void snp_update_svsm_ca(void);
+enum es_result sev_notify_savic_gpa(u64 gpa);
#else /* !CONFIG_AMD_MEM_ENCRYPT */
@@ -435,6 +436,7 @@ static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
static inline u64 sev_get_status(void) { return 0; }
static inline void sev_show_status(void) { }
static inline void snp_update_svsm_ca(void) { }
+static inline enum es_result sev_notify_savic_gpa(u64 gpa) { return ES_UNSUPPORTED; }
#endif /* CONFIG_AMD_MEM_ENCRYPT */
@@ -116,6 +116,7 @@
#define SVM_VMGEXIT_AP_CREATE 1
#define SVM_VMGEXIT_AP_DESTROY 2
#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018
+#define SVM_VMGEXIT_SECURE_AVIC_GPA 0x8000001a
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
@@ -1499,6 +1499,8 @@ static void setup_local_APIC(void)
return;
}
+ if (apic->setup)
+ apic->setup();
/*
* If this comes from kexec/kcrash the APIC might be enabled in
* SPIV. Soft disable it before doing further initialization.
@@ -9,12 +9,16 @@
#include <linux/cpumask.h>
#include <linux/cc_platform.h>
+#include <linux/percpu-defs.h>
#include <asm/apic.h>
#include <asm/sev.h>
#include "local.h"
+static DEFINE_PER_CPU(void *, apic_backing_page);
+static DEFINE_PER_CPU(bool, savic_setup_done);
+
static int x2apic_savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC);
@@ -61,8 +65,30 @@ static void x2apic_savic_send_IPI_mask_allbutself(const struct cpumask *mask, in
__send_IPI_mask(mask, vector, APIC_DEST_ALLBUT);
}
+static void x2apic_savic_setup(void)
+{
+ void *backing_page;
+ enum es_result ret;
+ unsigned long gpa;
+
+ if (this_cpu_read(savic_setup_done))
+ return;
+
+ backing_page = this_cpu_read(apic_backing_page);
+ gpa = __pa(backing_page);
+ ret = sev_notify_savic_gpa(gpa);
+ if (ret != ES_OK)
+ snp_abort();
+ this_cpu_write(savic_setup_done, true);
+}
+
static int x2apic_savic_probe(void)
{
+ void *backing_pages;
+ unsigned int cpu;
+ size_t sz;
+ int i;
+
if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
return 0;
@@ -71,6 +97,17 @@ static int x2apic_savic_probe(void)
snp_abort();
}
+ sz = ALIGN(num_possible_cpus() * SZ_4K, SZ_2M);
+ backing_pages = kzalloc(sz, GFP_ATOMIC);
+ if (!backing_pages)
+ snp_abort();
+
+ i = 0;
+ for_each_possible_cpu(cpu) {
+ per_cpu(apic_backing_page, cpu) = backing_pages + i * SZ_4K;
+ i++;
+ }
+
pr_info("Secure AVIC Enabled\n");
return 1;
@@ -81,6 +118,7 @@ static struct apic apic_x2apic_savic __ro_after_init = {
.name = "secure avic x2apic",
.probe = x2apic_savic_probe,
.acpi_madt_oem_check = x2apic_savic_acpi_madt_oem_check,
+ .setup = x2apic_savic_setup,
.dest_mode_logical = false,