mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
Merge branch 'x86/apic' into x86/sev, to resolve conflict
Conflicts: arch/x86/include/asm/sev-internal.h Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -483,6 +483,19 @@ config X86_X2APIC
|
||||
|
||||
If in doubt, say Y.
|
||||
|
||||
config AMD_SECURE_AVIC
|
||||
bool "AMD Secure AVIC"
|
||||
depends on AMD_MEM_ENCRYPT && X86_X2APIC
|
||||
help
|
||||
Enable this to get AMD Secure AVIC support on guests that have this feature.
|
||||
|
||||
AMD Secure AVIC provides hardware acceleration for performance sensitive
|
||||
APIC accesses and support for managing guest owned APIC state for SEV-SNP
|
||||
guests. Secure AVIC does not support xAPIC mode. It has functional
|
||||
dependency on x2apic being enabled in the guest.
|
||||
|
||||
If you don't know what to do here, say N.
|
||||
|
||||
config X86_POSTED_MSI
|
||||
bool "Enable MSI and MSI-x delivery by posted interrupts"
|
||||
depends on X86_64 && IRQ_REMAP
|
||||
|
||||
@@ -186,15 +186,23 @@ bool sev_es_check_ghcb_fault(unsigned long address)
|
||||
MSR_AMD64_SNP_VMSA_REG_PROT | \
|
||||
MSR_AMD64_SNP_RESERVED_BIT13 | \
|
||||
MSR_AMD64_SNP_RESERVED_BIT15 | \
|
||||
MSR_AMD64_SNP_SECURE_AVIC | \
|
||||
MSR_AMD64_SNP_RESERVED_MASK)
|
||||
|
||||
#ifdef CONFIG_AMD_SECURE_AVIC
|
||||
#define SNP_FEATURE_SECURE_AVIC MSR_AMD64_SNP_SECURE_AVIC
|
||||
#else
|
||||
#define SNP_FEATURE_SECURE_AVIC 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SNP_FEATURES_PRESENT is the mask of SNP features that are implemented
|
||||
* by the guest kernel. As and when a new feature is implemented in the
|
||||
* guest kernel, a corresponding bit should be added to the mask.
|
||||
*/
|
||||
#define SNP_FEATURES_PRESENT (MSR_AMD64_SNP_DEBUG_SWAP | \
|
||||
MSR_AMD64_SNP_SECURE_TSC)
|
||||
MSR_AMD64_SNP_SECURE_TSC | \
|
||||
SNP_FEATURE_SECURE_AVIC)
|
||||
|
||||
u64 snp_get_unsupported_features(u64 status)
|
||||
{
|
||||
|
||||
@@ -104,6 +104,9 @@ static bool noinstr amd_cc_platform_has(enum cc_attr attr)
|
||||
case CC_ATTR_HOST_SEV_SNP:
|
||||
return cc_flags.host_sev_snp;
|
||||
|
||||
case CC_ATTR_SNP_SECURE_AVIC:
|
||||
return sev_status & MSR_AMD64_SNP_SECURE_AVIC;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ static const char * const sev_status_feat_names[] = {
|
||||
[MSR_AMD64_SNP_IBS_VIRT_BIT] = "IBSVirt",
|
||||
[MSR_AMD64_SNP_VMSA_REG_PROT_BIT] = "VMSARegProt",
|
||||
[MSR_AMD64_SNP_SMT_PROT_BIT] = "SMTProt",
|
||||
[MSR_AMD64_SNP_SECURE_AVIC_BIT] = "SecureAVIC",
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1105,6 +1106,9 @@ static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip, unsigned
|
||||
vmsa->x87_ftw = AP_INIT_X87_FTW_DEFAULT;
|
||||
vmsa->x87_fcw = AP_INIT_X87_FCW_DEFAULT;
|
||||
|
||||
if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
|
||||
vmsa->vintr_ctrl |= V_GIF_MASK | V_NMI_ENABLE_MASK;
|
||||
|
||||
/* SVME must be set. */
|
||||
vmsa->efer = EFER_SVME;
|
||||
|
||||
@@ -1239,6 +1243,105 @@ int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 savic_ghcb_msr_read(u32 reg)
|
||||
{
|
||||
u64 msr = APIC_BASE_MSR + (reg >> 4);
|
||||
struct pt_regs regs = { .cx = msr };
|
||||
struct es_em_ctxt ctxt = { .regs = ®s };
|
||||
struct ghcb_state state;
|
||||
enum es_result res;
|
||||
struct ghcb *ghcb;
|
||||
|
||||
guard(irqsave)();
|
||||
|
||||
ghcb = __sev_get_ghcb(&state);
|
||||
vc_ghcb_invalidate(ghcb);
|
||||
|
||||
res = sev_es_ghcb_handle_msr(ghcb, &ctxt, false);
|
||||
if (res != ES_OK) {
|
||||
pr_err("Secure AVIC MSR (0x%llx) read returned error (%d)\n", msr, res);
|
||||
/* MSR read failures are treated as fatal errors */
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
|
||||
}
|
||||
|
||||
__sev_put_ghcb(&state);
|
||||
|
||||
return regs.ax | regs.dx << 32;
|
||||
}
|
||||
|
||||
void savic_ghcb_msr_write(u32 reg, u64 value)
|
||||
{
|
||||
u64 msr = APIC_BASE_MSR + (reg >> 4);
|
||||
struct pt_regs regs = {
|
||||
.cx = msr,
|
||||
.ax = lower_32_bits(value),
|
||||
.dx = upper_32_bits(value)
|
||||
};
|
||||
struct es_em_ctxt ctxt = { .regs = ®s };
|
||||
struct ghcb_state state;
|
||||
enum es_result res;
|
||||
struct ghcb *ghcb;
|
||||
|
||||
guard(irqsave)();
|
||||
|
||||
ghcb = __sev_get_ghcb(&state);
|
||||
vc_ghcb_invalidate(ghcb);
|
||||
|
||||
res = sev_es_ghcb_handle_msr(ghcb, &ctxt, true);
|
||||
if (res != ES_OK) {
|
||||
pr_err("Secure AVIC MSR (0x%llx) write returned error (%d)\n", msr, res);
|
||||
/* MSR writes should never fail. Any failure is fatal error for SNP guest */
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
|
||||
}
|
||||
|
||||
__sev_put_ghcb(&state);
|
||||
}
|
||||
|
||||
enum es_result savic_register_gpa(u64 gpa)
|
||||
{
|
||||
struct ghcb_state state;
|
||||
struct es_em_ctxt ctxt;
|
||||
enum es_result res;
|
||||
struct ghcb *ghcb;
|
||||
|
||||
guard(irqsave)();
|
||||
|
||||
ghcb = __sev_get_ghcb(&state);
|
||||
vc_ghcb_invalidate(ghcb);
|
||||
|
||||
ghcb_set_rax(ghcb, SVM_VMGEXIT_SAVIC_SELF_GPA);
|
||||
ghcb_set_rbx(ghcb, gpa);
|
||||
res = sev_es_ghcb_hv_call(ghcb, &ctxt, SVM_VMGEXIT_SAVIC,
|
||||
SVM_VMGEXIT_SAVIC_REGISTER_GPA, 0);
|
||||
|
||||
__sev_put_ghcb(&state);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
enum es_result savic_unregister_gpa(u64 *gpa)
|
||||
{
|
||||
struct ghcb_state state;
|
||||
struct es_em_ctxt ctxt;
|
||||
enum es_result res;
|
||||
struct ghcb *ghcb;
|
||||
|
||||
guard(irqsave)();
|
||||
|
||||
ghcb = __sev_get_ghcb(&state);
|
||||
vc_ghcb_invalidate(ghcb);
|
||||
|
||||
ghcb_set_rax(ghcb, SVM_VMGEXIT_SAVIC_SELF_GPA);
|
||||
res = sev_es_ghcb_hv_call(ghcb, &ctxt, SVM_VMGEXIT_SAVIC,
|
||||
SVM_VMGEXIT_SAVIC_UNREGISTER_GPA, 0);
|
||||
if (gpa && res == ES_OK)
|
||||
*gpa = ghcb->save.rbx;
|
||||
|
||||
__sev_put_ghcb(&state);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void snp_register_per_cpu_ghcb(void)
|
||||
{
|
||||
struct sev_es_runtime_data *data;
|
||||
|
||||
@@ -404,14 +404,10 @@ static enum es_result __vc_handle_secure_tsc_msrs(struct es_em_ctxt *ctxt, bool
|
||||
return ES_OK;
|
||||
}
|
||||
|
||||
static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||
enum es_result sev_es_ghcb_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt, bool write)
|
||||
{
|
||||
struct pt_regs *regs = ctxt->regs;
|
||||
enum es_result ret;
|
||||
bool write;
|
||||
|
||||
/* Is it a WRMSR? */
|
||||
write = ctxt->insn.opcode.bytes[1] == 0x30;
|
||||
|
||||
switch (regs->cx) {
|
||||
case MSR_SVSM_CAA:
|
||||
@@ -421,6 +417,15 @@ static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||
if (sev_status & MSR_AMD64_SNP_SECURE_TSC)
|
||||
return __vc_handle_secure_tsc_msrs(ctxt, write);
|
||||
break;
|
||||
case MSR_AMD64_SAVIC_CONTROL:
|
||||
/*
|
||||
* AMD64_SAVIC_CONTROL should not be intercepted when
|
||||
* Secure AVIC is enabled. Terminate the Secure AVIC guest
|
||||
* if the interception is enabled.
|
||||
*/
|
||||
if (cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
|
||||
return ES_VMM_ERROR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -441,6 +446,11 @@ static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||
{
|
||||
return sev_es_ghcb_handle_msr(ghcb, ctxt, ctxt->insn.opcode.bytes[1] == 0x30);
|
||||
}
|
||||
|
||||
static void __init vc_early_forward_exception(struct es_em_ctxt *ctxt)
|
||||
{
|
||||
int trapnr = ctxt->fi.vector;
|
||||
|
||||
@@ -305,6 +305,8 @@ struct apic {
|
||||
|
||||
/* Probe, setup and smpboot functions */
|
||||
int (*probe)(void);
|
||||
void (*setup)(void);
|
||||
void (*teardown)(void);
|
||||
int (*acpi_madt_oem_check)(char *oem_id, char *oem_table_id);
|
||||
|
||||
void (*init_apic_ldr)(void);
|
||||
@@ -317,6 +319,8 @@ struct apic {
|
||||
/* wakeup secondary CPU using 64-bit wakeup point */
|
||||
int (*wakeup_secondary_cpu_64)(u32 apicid, unsigned long start_eip, unsigned int cpu);
|
||||
|
||||
void (*update_vector)(unsigned int cpu, unsigned int vector, bool set);
|
||||
|
||||
char *name;
|
||||
};
|
||||
|
||||
@@ -470,6 +474,12 @@ static __always_inline bool apic_id_valid(u32 apic_id)
|
||||
return apic_id <= apic->max_apic_id;
|
||||
}
|
||||
|
||||
static __always_inline void apic_update_vector(unsigned int cpu, unsigned int vector, bool set)
|
||||
{
|
||||
if (apic->update_vector)
|
||||
apic->update_vector(cpu, vector, set);
|
||||
}
|
||||
|
||||
#else /* CONFIG_X86_LOCAL_APIC */
|
||||
|
||||
static inline u32 apic_read(u32 reg) { return 0; }
|
||||
@@ -481,6 +491,7 @@ static inline void apic_wait_icr_idle(void) { }
|
||||
static inline u32 safe_apic_wait_icr_idle(void) { return 0; }
|
||||
static inline void apic_native_eoi(void) { WARN_ON_ONCE(1); }
|
||||
static inline void apic_setup_apic_calls(void) { }
|
||||
static inline void apic_update_vector(unsigned int cpu, unsigned int vector, bool set) { }
|
||||
|
||||
#define apic_update_callback(_callback, _fn) do { } while (0)
|
||||
|
||||
|
||||
@@ -135,6 +135,8 @@
|
||||
#define APIC_TDR_DIV_128 0xA
|
||||
#define APIC_EFEAT 0x400
|
||||
#define APIC_ECTRL 0x410
|
||||
#define APIC_SEOI 0x420
|
||||
#define APIC_IER 0x480
|
||||
#define APIC_EILVTn(n) (0x500 + 0x10 * n)
|
||||
#define APIC_EILVT_NR_AMD_K8 1 /* # of extended interrupts */
|
||||
#define APIC_EILVT_NR_AMD_10H 4
|
||||
|
||||
@@ -699,8 +699,15 @@
|
||||
#define MSR_AMD64_SNP_VMSA_REG_PROT BIT_ULL(MSR_AMD64_SNP_VMSA_REG_PROT_BIT)
|
||||
#define MSR_AMD64_SNP_SMT_PROT_BIT 17
|
||||
#define MSR_AMD64_SNP_SMT_PROT BIT_ULL(MSR_AMD64_SNP_SMT_PROT_BIT)
|
||||
#define MSR_AMD64_SNP_RESV_BIT 18
|
||||
#define MSR_AMD64_SNP_SECURE_AVIC_BIT 18
|
||||
#define MSR_AMD64_SNP_SECURE_AVIC BIT_ULL(MSR_AMD64_SNP_SECURE_AVIC_BIT)
|
||||
#define MSR_AMD64_SNP_RESV_BIT 19
|
||||
#define MSR_AMD64_SNP_RESERVED_MASK GENMASK_ULL(63, MSR_AMD64_SNP_RESV_BIT)
|
||||
#define MSR_AMD64_SAVIC_CONTROL 0xc0010138
|
||||
#define MSR_AMD64_SAVIC_EN_BIT 0
|
||||
#define MSR_AMD64_SAVIC_EN BIT_ULL(MSR_AMD64_SAVIC_EN_BIT)
|
||||
#define MSR_AMD64_SAVIC_ALLOWEDNMI_BIT 1
|
||||
#define MSR_AMD64_SAVIC_ALLOWEDNMI BIT_ULL(MSR_AMD64_SAVIC_ALLOWEDNMI_BIT)
|
||||
#define MSR_AMD64_RMP_BASE 0xc0010132
|
||||
#define MSR_AMD64_RMP_END 0xc0010133
|
||||
#define MSR_AMD64_RMP_CFG 0xc0010136
|
||||
|
||||
@@ -208,6 +208,7 @@ struct snp_psc_desc {
|
||||
#define GHCB_TERM_SVSM_CAA 9 /* SVSM is present but CAA is not page aligned */
|
||||
#define GHCB_TERM_SECURE_TSC 10 /* Secure TSC initialization failed */
|
||||
#define GHCB_TERM_SVSM_CA_REMAP_FAIL 11 /* SVSM is present but CA could not be remapped */
|
||||
#define GHCB_TERM_SAVIC_FAIL 12 /* Secure AVIC-specific failure */
|
||||
|
||||
#define GHCB_RESP_CODE(v) ((v) & GHCB_MSR_INFO_MASK)
|
||||
|
||||
|
||||
@@ -80,6 +80,8 @@ static __always_inline void sev_es_wr_ghcb_msr(u64 val)
|
||||
native_wrmsr(MSR_AMD64_SEV_ES_GHCB, low, high);
|
||||
}
|
||||
|
||||
enum es_result sev_es_ghcb_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt, bool write);
|
||||
|
||||
u64 get_hv_features(void);
|
||||
|
||||
const struct snp_cpuid_table *snp_cpuid_get_table(void);
|
||||
|
||||
@@ -532,6 +532,10 @@ int snp_svsm_vtpm_send_command(u8 *buffer);
|
||||
|
||||
void __init snp_secure_tsc_prepare(void);
|
||||
void __init snp_secure_tsc_init(void);
|
||||
enum es_result savic_register_gpa(u64 gpa);
|
||||
enum es_result savic_unregister_gpa(u64 *gpa);
|
||||
u64 savic_ghcb_msr_read(u32 reg);
|
||||
void savic_ghcb_msr_write(u32 reg, u64 value);
|
||||
|
||||
static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
|
||||
{
|
||||
@@ -617,6 +621,10 @@ static inline int snp_send_guest_request(struct snp_msg_desc *mdesc,
|
||||
static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; }
|
||||
static inline void __init snp_secure_tsc_prepare(void) { }
|
||||
static inline void __init snp_secure_tsc_init(void) { }
|
||||
static inline enum es_result savic_register_gpa(u64 gpa) { return ES_UNSUPPORTED; }
|
||||
static inline enum es_result savic_unregister_gpa(u64 *gpa) { return ES_UNSUPPORTED; }
|
||||
static inline void savic_ghcb_msr_write(u32 reg, u64 value) { }
|
||||
static inline u64 savic_ghcb_msr_read(u32 reg) { return 0; }
|
||||
|
||||
#endif /* CONFIG_AMD_MEM_ENCRYPT */
|
||||
|
||||
|
||||
@@ -118,6 +118,10 @@
|
||||
#define SVM_VMGEXIT_AP_CREATE 1
|
||||
#define SVM_VMGEXIT_AP_DESTROY 2
|
||||
#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018
|
||||
#define SVM_VMGEXIT_SAVIC 0x8000001a
|
||||
#define SVM_VMGEXIT_SAVIC_REGISTER_GPA 0
|
||||
#define SVM_VMGEXIT_SAVIC_UNREGISTER_GPA 1
|
||||
#define SVM_VMGEXIT_SAVIC_SELF_GPA ~0ULL
|
||||
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
|
||||
#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
|
||||
#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
|
||||
|
||||
@@ -18,6 +18,7 @@ ifeq ($(CONFIG_X86_64),y)
|
||||
# APIC probe will depend on the listing order here
|
||||
obj-$(CONFIG_X86_NUMACHIP) += apic_numachip.o
|
||||
obj-$(CONFIG_X86_UV) += x2apic_uv_x.o
|
||||
obj-$(CONFIG_AMD_SECURE_AVIC) += x2apic_savic.o
|
||||
obj-$(CONFIG_X86_X2APIC) += x2apic_phys.o
|
||||
obj-$(CONFIG_X86_X2APIC) += x2apic_cluster.o
|
||||
obj-y += apic_flat_64.o
|
||||
|
||||
@@ -592,6 +592,8 @@ static void setup_APIC_timer(void)
|
||||
0xF, ~0UL);
|
||||
} else
|
||||
clockevents_register_device(levt);
|
||||
|
||||
apic_update_vector(smp_processor_id(), LOCAL_TIMER_VECTOR, true);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1168,6 +1170,9 @@ void disable_local_APIC(void)
|
||||
if (!apic_accessible())
|
||||
return;
|
||||
|
||||
if (apic->teardown)
|
||||
apic->teardown();
|
||||
|
||||
apic_soft_disable();
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
@@ -1428,63 +1433,61 @@ union apic_ir {
|
||||
u32 regs[APIC_IR_REGS];
|
||||
};
|
||||
|
||||
static bool apic_check_and_ack(union apic_ir *irr, union apic_ir *isr)
|
||||
static bool apic_check_and_eoi_isr(union apic_ir *isr)
|
||||
{
|
||||
int i, bit;
|
||||
|
||||
/* Read the IRRs */
|
||||
for (i = 0; i < APIC_IR_REGS; i++)
|
||||
irr->regs[i] = apic_read(APIC_IRR + i * 0x10);
|
||||
|
||||
/* Read the ISRs */
|
||||
for (i = 0; i < APIC_IR_REGS; i++)
|
||||
isr->regs[i] = apic_read(APIC_ISR + i * 0x10);
|
||||
|
||||
/*
|
||||
* If the ISR map is not empty. ACK the APIC and run another round
|
||||
* to verify whether a pending IRR has been unblocked and turned
|
||||
* into a ISR.
|
||||
*/
|
||||
if (!bitmap_empty(isr->map, APIC_IR_BITS)) {
|
||||
/*
|
||||
* There can be multiple ISR bits set when a high priority
|
||||
* interrupt preempted a lower priority one. Issue an ACK
|
||||
* per set bit.
|
||||
*/
|
||||
for_each_set_bit(bit, isr->map, APIC_IR_BITS)
|
||||
apic_eoi();
|
||||
/* If the ISR map empty, nothing to do here. */
|
||||
if (bitmap_empty(isr->map, APIC_IR_BITS))
|
||||
return true;
|
||||
}
|
||||
|
||||
return !bitmap_empty(irr->map, APIC_IR_BITS);
|
||||
/*
|
||||
* There can be multiple ISR bits set when a high priority
|
||||
* interrupt preempted a lower priority one. Issue an EOI for each
|
||||
* set bit. The priority traversal order does not matter as there
|
||||
* can't be new ISR bits raised at this point. What matters is that
|
||||
* an EOI is issued for each ISR bit.
|
||||
*/
|
||||
for_each_set_bit(bit, isr->map, APIC_IR_BITS)
|
||||
apic_eoi();
|
||||
|
||||
/* Reread the ISRs, they should be empty now */
|
||||
for (i = 0; i < APIC_IR_REGS; i++)
|
||||
isr->regs[i] = apic_read(APIC_ISR + i * 0x10);
|
||||
|
||||
return bitmap_empty(isr->map, APIC_IR_BITS);
|
||||
}
|
||||
|
||||
/*
|
||||
* After a crash, we no longer service the interrupts and a pending
|
||||
* interrupt from previous kernel might still have ISR bit set.
|
||||
* If a CPU services an interrupt and crashes before issuing EOI to the
|
||||
* local APIC, the corresponding ISR bit is still set when the crashing CPU
|
||||
* jumps into a crash kernel. Read the ISR and issue an EOI for each set
|
||||
* bit to acknowledge it as otherwise these slots would be locked forever
|
||||
* waiting for an EOI.
|
||||
*
|
||||
* Most probably by now the CPU has serviced that pending interrupt and it
|
||||
* might not have done the apic_eoi() because it thought, interrupt
|
||||
* came from i8259 as ExtInt. LAPIC did not get EOI so it does not clear
|
||||
* the ISR bit and cpu thinks it has already serviced the interrupt. Hence
|
||||
* a vector might get locked. It was noticed for timer irq (vector
|
||||
* 0x31). Issue an extra EOI to clear ISR.
|
||||
* If there are pending bits in the IRR, then they won't be converted into
|
||||
* ISR bits as the CPU has interrupts disabled. They will be delivered once
|
||||
* the CPU enables interrupts and there is nothing which can prevent that.
|
||||
*
|
||||
* If there are pending IRR bits they turn into ISR bits after a higher
|
||||
* priority ISR bit has been acked.
|
||||
* In the worst case this results in spurious interrupt warnings.
|
||||
*/
|
||||
static void apic_pending_intr_clear(void)
|
||||
static void apic_clear_isr(void)
|
||||
{
|
||||
union apic_ir irr, isr;
|
||||
union apic_ir ir;
|
||||
unsigned int i;
|
||||
|
||||
/* 512 loops are way oversized and give the APIC a chance to obey. */
|
||||
for (i = 0; i < 512; i++) {
|
||||
if (!apic_check_and_ack(&irr, &isr))
|
||||
return;
|
||||
}
|
||||
/* Dump the IRR/ISR content if that failed */
|
||||
pr_warn("APIC: Stale IRR: %256pb ISR: %256pb\n", irr.map, isr.map);
|
||||
if (!apic_check_and_eoi_isr(&ir))
|
||||
pr_warn("APIC: Stale ISR: %256pb\n", ir.map);
|
||||
|
||||
for (i = 0; i < APIC_IR_REGS; i++)
|
||||
ir.regs[i] = apic_read(APIC_IRR + i * 0x10);
|
||||
|
||||
if (!bitmap_empty(ir.map, APIC_IR_BITS))
|
||||
pr_warn("APIC: Stale IRR: %256pb\n", ir.map);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1503,6 +1506,9 @@ 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.
|
||||
@@ -1541,8 +1547,7 @@ static void setup_local_APIC(void)
|
||||
value |= 0x10;
|
||||
apic_write(APIC_TASKPRI, value);
|
||||
|
||||
/* Clear eventually stale ISR/IRR bits */
|
||||
apic_pending_intr_clear();
|
||||
apic_clear_isr();
|
||||
|
||||
/*
|
||||
* Now that we are all set up, enable the APIC
|
||||
|
||||
@@ -134,13 +134,20 @@ static void apic_update_irq_cfg(struct irq_data *irqd, unsigned int vector,
|
||||
|
||||
apicd->hw_irq_cfg.vector = vector;
|
||||
apicd->hw_irq_cfg.dest_apicid = apic->calc_dest_apicid(cpu);
|
||||
|
||||
apic_update_vector(cpu, vector, true);
|
||||
|
||||
irq_data_update_effective_affinity(irqd, cpumask_of(cpu));
|
||||
trace_vector_config(irqd->irq, vector, cpu,
|
||||
apicd->hw_irq_cfg.dest_apicid);
|
||||
trace_vector_config(irqd->irq, vector, cpu, apicd->hw_irq_cfg.dest_apicid);
|
||||
}
|
||||
|
||||
static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
|
||||
unsigned int newcpu)
|
||||
static void apic_free_vector(unsigned int cpu, unsigned int vector, bool managed)
|
||||
{
|
||||
apic_update_vector(cpu, vector, false);
|
||||
irq_matrix_free(vector_matrix, cpu, vector, managed);
|
||||
}
|
||||
|
||||
static void chip_data_update(struct irq_data *irqd, unsigned int newvec, unsigned int newcpu)
|
||||
{
|
||||
struct apic_chip_data *apicd = apic_chip_data(irqd);
|
||||
struct irq_desc *desc = irq_data_to_desc(irqd);
|
||||
@@ -174,8 +181,7 @@ static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
|
||||
apicd->prev_cpu = apicd->cpu;
|
||||
WARN_ON_ONCE(apicd->cpu == newcpu);
|
||||
} else {
|
||||
irq_matrix_free(vector_matrix, apicd->cpu, apicd->vector,
|
||||
managed);
|
||||
apic_free_vector(apicd->cpu, apicd->vector, managed);
|
||||
}
|
||||
|
||||
setnew:
|
||||
@@ -261,7 +267,7 @@ assign_vector_locked(struct irq_data *irqd, const struct cpumask *dest)
|
||||
trace_vector_alloc(irqd->irq, vector, resvd, vector);
|
||||
if (vector < 0)
|
||||
return vector;
|
||||
apic_update_vector(irqd, vector, cpu);
|
||||
chip_data_update(irqd, vector, cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -337,7 +343,7 @@ assign_managed_vector(struct irq_data *irqd, const struct cpumask *dest)
|
||||
trace_vector_alloc_managed(irqd->irq, vector, vector);
|
||||
if (vector < 0)
|
||||
return vector;
|
||||
apic_update_vector(irqd, vector, cpu);
|
||||
chip_data_update(irqd, vector, cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -357,7 +363,7 @@ static void clear_irq_vector(struct irq_data *irqd)
|
||||
apicd->prev_cpu);
|
||||
|
||||
per_cpu(vector_irq, apicd->cpu)[vector] = VECTOR_SHUTDOWN;
|
||||
irq_matrix_free(vector_matrix, apicd->cpu, vector, managed);
|
||||
apic_free_vector(apicd->cpu, vector, managed);
|
||||
apicd->vector = 0;
|
||||
|
||||
/* Clean up move in progress */
|
||||
@@ -366,7 +372,7 @@ static void clear_irq_vector(struct irq_data *irqd)
|
||||
return;
|
||||
|
||||
per_cpu(vector_irq, apicd->prev_cpu)[vector] = VECTOR_SHUTDOWN;
|
||||
irq_matrix_free(vector_matrix, apicd->prev_cpu, vector, managed);
|
||||
apic_free_vector(apicd->prev_cpu, vector, managed);
|
||||
apicd->prev_vector = 0;
|
||||
apicd->move_in_progress = 0;
|
||||
hlist_del_init(&apicd->clist);
|
||||
@@ -905,7 +911,7 @@ static void free_moved_vector(struct apic_chip_data *apicd)
|
||||
* affinity mask comes online.
|
||||
*/
|
||||
trace_vector_free_moved(apicd->irq, cpu, vector, managed);
|
||||
irq_matrix_free(vector_matrix, cpu, vector, managed);
|
||||
apic_free_vector(cpu, vector, managed);
|
||||
per_cpu(vector_irq, cpu)[vector] = VECTOR_UNUSED;
|
||||
hlist_del_init(&apicd->clist);
|
||||
apicd->prev_vector = 0;
|
||||
|
||||
428
arch/x86/kernel/apic/x2apic_savic.c
Normal file
428
arch/x86/kernel/apic/x2apic_savic.c
Normal file
@@ -0,0 +1,428 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* AMD Secure AVIC Support (SEV-SNP Guests)
|
||||
*
|
||||
* Copyright (C) 2024 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/cc_platform.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/percpu-defs.h>
|
||||
#include <linux/align.h>
|
||||
|
||||
#include <asm/apic.h>
|
||||
#include <asm/sev.h>
|
||||
|
||||
#include "local.h"
|
||||
|
||||
struct secure_avic_page {
|
||||
u8 regs[PAGE_SIZE];
|
||||
} __aligned(PAGE_SIZE);
|
||||
|
||||
static struct secure_avic_page __percpu *savic_page __ro_after_init;
|
||||
|
||||
static int savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
|
||||
{
|
||||
return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC);
|
||||
}
|
||||
|
||||
static inline void *get_reg_bitmap(unsigned int cpu, unsigned int offset)
|
||||
{
|
||||
return &per_cpu_ptr(savic_page, cpu)->regs[offset];
|
||||
}
|
||||
|
||||
static inline void update_vector(unsigned int cpu, unsigned int offset,
|
||||
unsigned int vector, bool set)
|
||||
{
|
||||
void *bitmap = get_reg_bitmap(cpu, offset);
|
||||
|
||||
if (set)
|
||||
apic_set_vector(vector, bitmap);
|
||||
else
|
||||
apic_clear_vector(vector, bitmap);
|
||||
}
|
||||
|
||||
#define SAVIC_ALLOWED_IRR 0x204
|
||||
|
||||
/*
|
||||
* When Secure AVIC is enabled, RDMSR/WRMSR of the APIC registers
|
||||
* result in #VC exception (for non-accelerated register accesses)
|
||||
* with VMEXIT_AVIC_NOACCEL error code. The #VC exception handler
|
||||
* can read/write the x2APIC register in the guest APIC backing page.
|
||||
*
|
||||
* Since doing this would increase the latency of accessing x2APIC
|
||||
* registers, instead of doing RDMSR/WRMSR based accesses and
|
||||
* handling the APIC register reads/writes in the #VC exception handler,
|
||||
* the read() and write() callbacks directly read/write the APIC register
|
||||
* from/to the vCPU's APIC backing page.
|
||||
*/
|
||||
static u32 savic_read(u32 reg)
|
||||
{
|
||||
void *ap = this_cpu_ptr(savic_page);
|
||||
|
||||
switch (reg) {
|
||||
case APIC_LVTT:
|
||||
case APIC_TMICT:
|
||||
case APIC_TMCCT:
|
||||
case APIC_TDCR:
|
||||
case APIC_LVTTHMR:
|
||||
case APIC_LVTPC:
|
||||
case APIC_LVT0:
|
||||
case APIC_LVT1:
|
||||
case APIC_LVTERR:
|
||||
return savic_ghcb_msr_read(reg);
|
||||
case APIC_ID:
|
||||
case APIC_LVR:
|
||||
case APIC_TASKPRI:
|
||||
case APIC_ARBPRI:
|
||||
case APIC_PROCPRI:
|
||||
case APIC_LDR:
|
||||
case APIC_SPIV:
|
||||
case APIC_ESR:
|
||||
case APIC_EFEAT:
|
||||
case APIC_ECTRL:
|
||||
case APIC_SEOI:
|
||||
case APIC_IER:
|
||||
case APIC_EILVTn(0) ... APIC_EILVTn(3):
|
||||
return apic_get_reg(ap, reg);
|
||||
case APIC_ICR:
|
||||
return (u32)apic_get_reg64(ap, reg);
|
||||
case APIC_ISR ... APIC_ISR + 0x70:
|
||||
case APIC_TMR ... APIC_TMR + 0x70:
|
||||
if (WARN_ONCE(!IS_ALIGNED(reg, 16),
|
||||
"APIC register read offset 0x%x not aligned at 16 bytes", reg))
|
||||
return 0;
|
||||
return apic_get_reg(ap, reg);
|
||||
/* IRR and ALLOWED_IRR offset range */
|
||||
case APIC_IRR ... APIC_IRR + 0x74:
|
||||
/*
|
||||
* Valid APIC_IRR/SAVIC_ALLOWED_IRR registers are at 16 bytes strides from
|
||||
* their respective base offset. APIC_IRRs are in the range
|
||||
*
|
||||
* (0x200, 0x210, ..., 0x270)
|
||||
*
|
||||
* while the SAVIC_ALLOWED_IRR range starts 4 bytes later, in the range
|
||||
*
|
||||
* (0x204, 0x214, ..., 0x274).
|
||||
*
|
||||
* Filter out everything else.
|
||||
*/
|
||||
if (WARN_ONCE(!(IS_ALIGNED(reg, 16) ||
|
||||
IS_ALIGNED(reg - 4, 16)),
|
||||
"Misaligned APIC_IRR/ALLOWED_IRR APIC register read offset 0x%x", reg))
|
||||
return 0;
|
||||
return apic_get_reg(ap, reg);
|
||||
default:
|
||||
pr_err("Error reading unknown Secure AVIC reg offset 0x%x\n", reg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define SAVIC_NMI_REQ 0x278
|
||||
|
||||
/*
|
||||
* On WRMSR to APIC_SELF_IPI register by the guest, Secure AVIC hardware
|
||||
* updates the APIC_IRR in the APIC backing page of the vCPU. In addition,
|
||||
* hardware evaluates the new APIC_IRR update for interrupt injection to
|
||||
* the vCPU. So, self IPIs are hardware-accelerated.
|
||||
*/
|
||||
static inline void self_ipi_reg_write(unsigned int vector)
|
||||
{
|
||||
native_apic_msr_write(APIC_SELF_IPI, vector);
|
||||
}
|
||||
|
||||
static void send_ipi_dest(unsigned int cpu, unsigned int vector, bool nmi)
|
||||
{
|
||||
if (nmi)
|
||||
apic_set_reg(per_cpu_ptr(savic_page, cpu), SAVIC_NMI_REQ, 1);
|
||||
else
|
||||
update_vector(cpu, APIC_IRR, vector, true);
|
||||
}
|
||||
|
||||
static void send_ipi_allbut(unsigned int vector, bool nmi)
|
||||
{
|
||||
unsigned int cpu, src_cpu;
|
||||
|
||||
guard(irqsave)();
|
||||
|
||||
src_cpu = raw_smp_processor_id();
|
||||
|
||||
for_each_cpu(cpu, cpu_online_mask) {
|
||||
if (cpu == src_cpu)
|
||||
continue;
|
||||
send_ipi_dest(cpu, vector, nmi);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void self_ipi(unsigned int vector, bool nmi)
|
||||
{
|
||||
u32 icr_low = APIC_SELF_IPI | vector;
|
||||
|
||||
if (nmi)
|
||||
icr_low |= APIC_DM_NMI;
|
||||
|
||||
native_x2apic_icr_write(icr_low, 0);
|
||||
}
|
||||
|
||||
static void savic_icr_write(u32 icr_low, u32 icr_high)
|
||||
{
|
||||
unsigned int dsh, vector;
|
||||
u64 icr_data;
|
||||
bool nmi;
|
||||
|
||||
dsh = icr_low & APIC_DEST_ALLBUT;
|
||||
vector = icr_low & APIC_VECTOR_MASK;
|
||||
nmi = ((icr_low & APIC_DM_FIXED_MASK) == APIC_DM_NMI);
|
||||
|
||||
switch (dsh) {
|
||||
case APIC_DEST_SELF:
|
||||
self_ipi(vector, nmi);
|
||||
break;
|
||||
case APIC_DEST_ALLINC:
|
||||
self_ipi(vector, nmi);
|
||||
fallthrough;
|
||||
case APIC_DEST_ALLBUT:
|
||||
send_ipi_allbut(vector, nmi);
|
||||
break;
|
||||
default:
|
||||
send_ipi_dest(icr_high, vector, nmi);
|
||||
break;
|
||||
}
|
||||
|
||||
icr_data = ((u64)icr_high) << 32 | icr_low;
|
||||
if (dsh != APIC_DEST_SELF)
|
||||
savic_ghcb_msr_write(APIC_ICR, icr_data);
|
||||
apic_set_reg64(this_cpu_ptr(savic_page), APIC_ICR, icr_data);
|
||||
}
|
||||
|
||||
static void savic_write(u32 reg, u32 data)
|
||||
{
|
||||
void *ap = this_cpu_ptr(savic_page);
|
||||
|
||||
switch (reg) {
|
||||
case APIC_LVTT:
|
||||
case APIC_TMICT:
|
||||
case APIC_TDCR:
|
||||
case APIC_LVT0:
|
||||
case APIC_LVT1:
|
||||
case APIC_LVTTHMR:
|
||||
case APIC_LVTPC:
|
||||
case APIC_LVTERR:
|
||||
savic_ghcb_msr_write(reg, data);
|
||||
break;
|
||||
case APIC_TASKPRI:
|
||||
case APIC_EOI:
|
||||
case APIC_SPIV:
|
||||
case SAVIC_NMI_REQ:
|
||||
case APIC_ESR:
|
||||
case APIC_ECTRL:
|
||||
case APIC_SEOI:
|
||||
case APIC_IER:
|
||||
case APIC_EILVTn(0) ... APIC_EILVTn(3):
|
||||
apic_set_reg(ap, reg, data);
|
||||
break;
|
||||
case APIC_ICR:
|
||||
savic_icr_write(data, 0);
|
||||
break;
|
||||
case APIC_SELF_IPI:
|
||||
self_ipi_reg_write(data);
|
||||
break;
|
||||
/* ALLOWED_IRR offsets are writable */
|
||||
case SAVIC_ALLOWED_IRR ... SAVIC_ALLOWED_IRR + 0x70:
|
||||
if (IS_ALIGNED(reg - 4, 16)) {
|
||||
apic_set_reg(ap, reg, data);
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
pr_err("Error writing unknown Secure AVIC reg offset 0x%x\n", reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_ipi(u32 dest, unsigned int vector, unsigned int dsh)
|
||||
{
|
||||
unsigned int icr_low;
|
||||
|
||||
icr_low = __prepare_ICR(dsh, vector, APIC_DEST_PHYSICAL);
|
||||
savic_icr_write(icr_low, dest);
|
||||
}
|
||||
|
||||
static void savic_send_ipi(int cpu, int vector)
|
||||
{
|
||||
u32 dest = per_cpu(x86_cpu_to_apicid, cpu);
|
||||
|
||||
send_ipi(dest, vector, 0);
|
||||
}
|
||||
|
||||
static void send_ipi_mask(const struct cpumask *mask, unsigned int vector, bool excl_self)
|
||||
{
|
||||
unsigned int cpu, this_cpu;
|
||||
|
||||
guard(irqsave)();
|
||||
|
||||
this_cpu = raw_smp_processor_id();
|
||||
|
||||
for_each_cpu(cpu, mask) {
|
||||
if (excl_self && cpu == this_cpu)
|
||||
continue;
|
||||
send_ipi(per_cpu(x86_cpu_to_apicid, cpu), vector, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void savic_send_ipi_mask(const struct cpumask *mask, int vector)
|
||||
{
|
||||
send_ipi_mask(mask, vector, false);
|
||||
}
|
||||
|
||||
static void savic_send_ipi_mask_allbutself(const struct cpumask *mask, int vector)
|
||||
{
|
||||
send_ipi_mask(mask, vector, true);
|
||||
}
|
||||
|
||||
static void savic_send_ipi_allbutself(int vector)
|
||||
{
|
||||
send_ipi(0, vector, APIC_DEST_ALLBUT);
|
||||
}
|
||||
|
||||
static void savic_send_ipi_all(int vector)
|
||||
{
|
||||
send_ipi(0, vector, APIC_DEST_ALLINC);
|
||||
}
|
||||
|
||||
static void savic_send_ipi_self(int vector)
|
||||
{
|
||||
self_ipi_reg_write(vector);
|
||||
}
|
||||
|
||||
static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set)
|
||||
{
|
||||
update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set);
|
||||
}
|
||||
|
||||
static void savic_eoi(void)
|
||||
{
|
||||
unsigned int cpu;
|
||||
int vec;
|
||||
|
||||
cpu = raw_smp_processor_id();
|
||||
vec = apic_find_highest_vector(get_reg_bitmap(cpu, APIC_ISR));
|
||||
if (WARN_ONCE(vec == -1, "EOI write while no active interrupt in APIC_ISR"))
|
||||
return;
|
||||
|
||||
/* Is level-triggered interrupt? */
|
||||
if (apic_test_vector(vec, get_reg_bitmap(cpu, APIC_TMR))) {
|
||||
update_vector(cpu, APIC_ISR, vec, false);
|
||||
/*
|
||||
* Propagate the EOI write to the hypervisor for level-triggered
|
||||
* interrupts. Return to the guest from GHCB protocol event takes
|
||||
* care of re-evaluating interrupt state.
|
||||
*/
|
||||
savic_ghcb_msr_write(APIC_EOI, 0);
|
||||
} else {
|
||||
/*
|
||||
* Hardware clears APIC_ISR and re-evaluates the interrupt state
|
||||
* to determine if there is any pending interrupt which can be
|
||||
* delivered to CPU.
|
||||
*/
|
||||
native_apic_msr_eoi();
|
||||
}
|
||||
}
|
||||
|
||||
static void savic_teardown(void)
|
||||
{
|
||||
/* Disable Secure AVIC */
|
||||
native_wrmsrq(MSR_AMD64_SAVIC_CONTROL, 0);
|
||||
savic_unregister_gpa(NULL);
|
||||
}
|
||||
|
||||
static void savic_setup(void)
|
||||
{
|
||||
void *ap = this_cpu_ptr(savic_page);
|
||||
enum es_result res;
|
||||
unsigned long gpa;
|
||||
|
||||
/*
|
||||
* Before Secure AVIC is enabled, APIC MSR reads are intercepted.
|
||||
* APIC_ID MSR read returns the value from the hypervisor.
|
||||
*/
|
||||
apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID));
|
||||
|
||||
gpa = __pa(ap);
|
||||
|
||||
/*
|
||||
* The NPT entry for a vCPU's APIC backing page must always be
|
||||
* present when the vCPU is running in order for Secure AVIC to
|
||||
* function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot
|
||||
* be resumed if the NPT entry for the APIC backing page is not
|
||||
* present. Notify GPA of the vCPU's APIC backing page to the
|
||||
* hypervisor by calling savic_register_gpa(). Before executing
|
||||
* VMRUN, the hypervisor makes use of this information to make sure
|
||||
* the APIC backing page is mapped in NPT.
|
||||
*/
|
||||
res = savic_register_gpa(gpa);
|
||||
if (res != ES_OK)
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
|
||||
|
||||
native_wrmsrq(MSR_AMD64_SAVIC_CONTROL,
|
||||
gpa | MSR_AMD64_SAVIC_EN | MSR_AMD64_SAVIC_ALLOWEDNMI);
|
||||
}
|
||||
|
||||
static int savic_probe(void)
|
||||
{
|
||||
if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
|
||||
return 0;
|
||||
|
||||
if (!x2apic_mode) {
|
||||
pr_err("Secure AVIC enabled in non x2APIC mode\n");
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
|
||||
/* unreachable */
|
||||
}
|
||||
|
||||
savic_page = alloc_percpu(struct secure_avic_page);
|
||||
if (!savic_page)
|
||||
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct apic apic_x2apic_savic __ro_after_init = {
|
||||
|
||||
.name = "secure avic x2apic",
|
||||
.probe = savic_probe,
|
||||
.acpi_madt_oem_check = savic_acpi_madt_oem_check,
|
||||
.setup = savic_setup,
|
||||
.teardown = savic_teardown,
|
||||
|
||||
.dest_mode_logical = false,
|
||||
|
||||
.disable_esr = 0,
|
||||
|
||||
.cpu_present_to_apicid = default_cpu_present_to_apicid,
|
||||
|
||||
.max_apic_id = UINT_MAX,
|
||||
.x2apic_set_max_apicid = true,
|
||||
.get_apic_id = x2apic_get_apic_id,
|
||||
|
||||
.calc_dest_apicid = apic_default_calc_apicid,
|
||||
|
||||
.send_IPI = savic_send_ipi,
|
||||
.send_IPI_mask = savic_send_ipi_mask,
|
||||
.send_IPI_mask_allbutself = savic_send_ipi_mask_allbutself,
|
||||
.send_IPI_allbutself = savic_send_ipi_allbutself,
|
||||
.send_IPI_all = savic_send_ipi_all,
|
||||
.send_IPI_self = savic_send_ipi_self,
|
||||
|
||||
.nmi_to_offline_cpu = true,
|
||||
|
||||
.read = savic_read,
|
||||
.write = savic_write,
|
||||
.eoi = savic_eoi,
|
||||
.icr_read = native_x2apic_icr_read,
|
||||
.icr_write = savic_icr_write,
|
||||
|
||||
.update_vector = savic_update_vector,
|
||||
};
|
||||
|
||||
apic_driver(apic_x2apic_savic);
|
||||
@@ -96,6 +96,14 @@ enum cc_attr {
|
||||
* enabled to run SEV-SNP guests.
|
||||
*/
|
||||
CC_ATTR_HOST_SEV_SNP,
|
||||
|
||||
/**
|
||||
* @CC_ATTR_SNP_SECURE_AVIC: Secure AVIC mode is active.
|
||||
*
|
||||
* The host kernel is running with the necessary features enabled
|
||||
* to run SEV-SNP guests with full Secure AVIC capabilities.
|
||||
*/
|
||||
CC_ATTR_SNP_SECURE_AVIC,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_CC_PLATFORM
|
||||
|
||||
Reference in New Issue
Block a user