138918e0bSBorislav Petkov (AMD) // SPDX-License-Identifier: GPL-2.0-only
238918e0bSBorislav Petkov (AMD) /*
338918e0bSBorislav Petkov (AMD) * AMD Memory Encryption Support
438918e0bSBorislav Petkov (AMD) *
538918e0bSBorislav Petkov (AMD) * Copyright (C) 2019 SUSE
638918e0bSBorislav Petkov (AMD) *
738918e0bSBorislav Petkov (AMD) * Author: Joerg Roedel <jroedel@suse.de>
838918e0bSBorislav Petkov (AMD) */
938918e0bSBorislav Petkov (AMD)
1038918e0bSBorislav Petkov (AMD) #define pr_fmt(fmt) "SEV: " fmt
1138918e0bSBorislav Petkov (AMD)
1238918e0bSBorislav Petkov (AMD) #include <linux/sched/debug.h> /* For show_regs() */
1338918e0bSBorislav Petkov (AMD) #include <linux/percpu-defs.h>
1438918e0bSBorislav Petkov (AMD) #include <linux/cc_platform.h>
1538918e0bSBorislav Petkov (AMD) #include <linux/printk.h>
1638918e0bSBorislav Petkov (AMD) #include <linux/mm_types.h>
1738918e0bSBorislav Petkov (AMD) #include <linux/set_memory.h>
1838918e0bSBorislav Petkov (AMD) #include <linux/memblock.h>
1938918e0bSBorislav Petkov (AMD) #include <linux/kernel.h>
2038918e0bSBorislav Petkov (AMD) #include <linux/mm.h>
2138918e0bSBorislav Petkov (AMD) #include <linux/cpumask.h>
2238918e0bSBorislav Petkov (AMD) #include <linux/efi.h>
2338918e0bSBorislav Petkov (AMD) #include <linux/platform_device.h>
2438918e0bSBorislav Petkov (AMD) #include <linux/io.h>
2538918e0bSBorislav Petkov (AMD) #include <linux/psp-sev.h>
2638918e0bSBorislav Petkov (AMD) #include <linux/dmi.h>
2738918e0bSBorislav Petkov (AMD) #include <uapi/linux/sev-guest.h>
2838918e0bSBorislav Petkov (AMD)
2938918e0bSBorislav Petkov (AMD) #include <asm/init.h>
3038918e0bSBorislav Petkov (AMD) #include <asm/cpu_entry_area.h>
3138918e0bSBorislav Petkov (AMD) #include <asm/stacktrace.h>
3238918e0bSBorislav Petkov (AMD) #include <asm/sev.h>
3338918e0bSBorislav Petkov (AMD) #include <asm/insn-eval.h>
3438918e0bSBorislav Petkov (AMD) #include <asm/fpu/xcr.h>
3538918e0bSBorislav Petkov (AMD) #include <asm/processor.h>
3638918e0bSBorislav Petkov (AMD) #include <asm/realmode.h>
3738918e0bSBorislav Petkov (AMD) #include <asm/setup.h>
3838918e0bSBorislav Petkov (AMD) #include <asm/traps.h>
3938918e0bSBorislav Petkov (AMD) #include <asm/svm.h>
4038918e0bSBorislav Petkov (AMD) #include <asm/smp.h>
4138918e0bSBorislav Petkov (AMD) #include <asm/cpu.h>
4238918e0bSBorislav Petkov (AMD) #include <asm/apic.h>
4338918e0bSBorislav Petkov (AMD) #include <asm/cpuid.h>
4438918e0bSBorislav Petkov (AMD) #include <asm/cmdline.h>
4538918e0bSBorislav Petkov (AMD)
4638918e0bSBorislav Petkov (AMD) #define DR7_RESET_VALUE 0x400
4738918e0bSBorislav Petkov (AMD)
4838918e0bSBorislav Petkov (AMD) /* AP INIT values as documented in the APM2 section "Processor Initialization State" */
4938918e0bSBorislav Petkov (AMD) #define AP_INIT_CS_LIMIT 0xffff
5038918e0bSBorislav Petkov (AMD) #define AP_INIT_DS_LIMIT 0xffff
5138918e0bSBorislav Petkov (AMD) #define AP_INIT_LDTR_LIMIT 0xffff
5238918e0bSBorislav Petkov (AMD) #define AP_INIT_GDTR_LIMIT 0xffff
5338918e0bSBorislav Petkov (AMD) #define AP_INIT_IDTR_LIMIT 0xffff
5438918e0bSBorislav Petkov (AMD) #define AP_INIT_TR_LIMIT 0xffff
5538918e0bSBorislav Petkov (AMD) #define AP_INIT_RFLAGS_DEFAULT 0x2
5638918e0bSBorislav Petkov (AMD) #define AP_INIT_DR6_DEFAULT 0xffff0ff0
5738918e0bSBorislav Petkov (AMD) #define AP_INIT_GPAT_DEFAULT 0x0007040600070406ULL
5838918e0bSBorislav Petkov (AMD) #define AP_INIT_XCR0_DEFAULT 0x1
5938918e0bSBorislav Petkov (AMD) #define AP_INIT_X87_FTW_DEFAULT 0x5555
6038918e0bSBorislav Petkov (AMD) #define AP_INIT_X87_FCW_DEFAULT 0x0040
6138918e0bSBorislav Petkov (AMD) #define AP_INIT_CR0_DEFAULT 0x60000010
6238918e0bSBorislav Petkov (AMD) #define AP_INIT_MXCSR_DEFAULT 0x1f80
6338918e0bSBorislav Petkov (AMD)
6438918e0bSBorislav Petkov (AMD) static const char * const sev_status_feat_names[] = {
6538918e0bSBorislav Petkov (AMD) [MSR_AMD64_SEV_ENABLED_BIT] = "SEV",
6638918e0bSBorislav Petkov (AMD) [MSR_AMD64_SEV_ES_ENABLED_BIT] = "SEV-ES",
6738918e0bSBorislav Petkov (AMD) [MSR_AMD64_SEV_SNP_ENABLED_BIT] = "SEV-SNP",
6838918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_VTOM_BIT] = "vTom",
6938918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_REFLECT_VC_BIT] = "ReflectVC",
7038918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_RESTRICTED_INJ_BIT] = "RI",
7138918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_ALT_INJ_BIT] = "AI",
7238918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_DEBUG_SWAP_BIT] = "DebugSwap",
7338918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_PREVENT_HOST_IBS_BIT] = "NoHostIBS",
7438918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_BTB_ISOLATION_BIT] = "BTBIsol",
7538918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_VMPL_SSS_BIT] = "VmplSSS",
7638918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_SECURE_TSC_BIT] = "SecureTSC",
7738918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_VMGEXIT_PARAM_BIT] = "VMGExitParam",
7838918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_IBS_VIRT_BIT] = "IBSVirt",
7938918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_VMSA_REG_PROT_BIT] = "VMSARegProt",
8038918e0bSBorislav Petkov (AMD) [MSR_AMD64_SNP_SMT_PROT_BIT] = "SMTProt",
8138918e0bSBorislav Petkov (AMD) };
8238918e0bSBorislav Petkov (AMD)
8338918e0bSBorislav Petkov (AMD) /* For early boot hypervisor communication in SEV-ES enabled guests */
8438918e0bSBorislav Petkov (AMD) static struct ghcb boot_ghcb_page __bss_decrypted __aligned(PAGE_SIZE);
8538918e0bSBorislav Petkov (AMD)
8638918e0bSBorislav Petkov (AMD) /*
8738918e0bSBorislav Petkov (AMD) * Needs to be in the .data section because we need it NULL before bss is
8838918e0bSBorislav Petkov (AMD) * cleared
8938918e0bSBorislav Petkov (AMD) */
9038918e0bSBorislav Petkov (AMD) static struct ghcb *boot_ghcb __section(".data");
9138918e0bSBorislav Petkov (AMD)
9238918e0bSBorislav Petkov (AMD) /* Bitmap of SEV features supported by the hypervisor */
9338918e0bSBorislav Petkov (AMD) static u64 sev_hv_features __ro_after_init;
9438918e0bSBorislav Petkov (AMD)
9538918e0bSBorislav Petkov (AMD) /* #VC handler runtime per-CPU data */
9638918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data {
9738918e0bSBorislav Petkov (AMD) struct ghcb ghcb_page;
9838918e0bSBorislav Petkov (AMD)
9938918e0bSBorislav Petkov (AMD) /*
10038918e0bSBorislav Petkov (AMD) * Reserve one page per CPU as backup storage for the unencrypted GHCB.
10138918e0bSBorislav Petkov (AMD) * It is needed when an NMI happens while the #VC handler uses the real
10238918e0bSBorislav Petkov (AMD) * GHCB, and the NMI handler itself is causing another #VC exception. In
10338918e0bSBorislav Petkov (AMD) * that case the GHCB content of the first handler needs to be backed up
10438918e0bSBorislav Petkov (AMD) * and restored.
10538918e0bSBorislav Petkov (AMD) */
10638918e0bSBorislav Petkov (AMD) struct ghcb backup_ghcb;
10738918e0bSBorislav Petkov (AMD)
10838918e0bSBorislav Petkov (AMD) /*
10938918e0bSBorislav Petkov (AMD) * Mark the per-cpu GHCBs as in-use to detect nested #VC exceptions.
11038918e0bSBorislav Petkov (AMD) * There is no need for it to be atomic, because nothing is written to
11138918e0bSBorislav Petkov (AMD) * the GHCB between the read and the write of ghcb_active. So it is safe
11238918e0bSBorislav Petkov (AMD) * to use it when a nested #VC exception happens before the write.
11338918e0bSBorislav Petkov (AMD) *
11438918e0bSBorislav Petkov (AMD) * This is necessary for example in the #VC->NMI->#VC case when the NMI
11538918e0bSBorislav Petkov (AMD) * happens while the first #VC handler uses the GHCB. When the NMI code
11638918e0bSBorislav Petkov (AMD) * raises a second #VC handler it might overwrite the contents of the
11738918e0bSBorislav Petkov (AMD) * GHCB written by the first handler. To avoid this the content of the
11838918e0bSBorislav Petkov (AMD) * GHCB is saved and restored when the GHCB is detected to be in use
11938918e0bSBorislav Petkov (AMD) * already.
12038918e0bSBorislav Petkov (AMD) */
12138918e0bSBorislav Petkov (AMD) bool ghcb_active;
12238918e0bSBorislav Petkov (AMD) bool backup_ghcb_active;
12338918e0bSBorislav Petkov (AMD)
12438918e0bSBorislav Petkov (AMD) /*
12538918e0bSBorislav Petkov (AMD) * Cached DR7 value - write it on DR7 writes and return it on reads.
12638918e0bSBorislav Petkov (AMD) * That value will never make it to the real hardware DR7 as debugging
12738918e0bSBorislav Petkov (AMD) * is currently unsupported in SEV-ES guests.
12838918e0bSBorislav Petkov (AMD) */
12938918e0bSBorislav Petkov (AMD) unsigned long dr7;
13038918e0bSBorislav Petkov (AMD) };
13138918e0bSBorislav Petkov (AMD)
13238918e0bSBorislav Petkov (AMD) struct ghcb_state {
13338918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
13438918e0bSBorislav Petkov (AMD) };
13538918e0bSBorislav Petkov (AMD)
13638918e0bSBorislav Petkov (AMD) /* For early boot SVSM communication */
13738918e0bSBorislav Petkov (AMD) static struct svsm_ca boot_svsm_ca_page __aligned(PAGE_SIZE);
13838918e0bSBorislav Petkov (AMD)
13938918e0bSBorislav Petkov (AMD) static DEFINE_PER_CPU(struct sev_es_runtime_data*, runtime_data);
14038918e0bSBorislav Petkov (AMD) static DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa);
14138918e0bSBorislav Petkov (AMD) static DEFINE_PER_CPU(struct svsm_ca *, svsm_caa);
14238918e0bSBorislav Petkov (AMD) static DEFINE_PER_CPU(u64, svsm_caa_pa);
14338918e0bSBorislav Petkov (AMD)
14438918e0bSBorislav Petkov (AMD) struct sev_config {
14538918e0bSBorislav Petkov (AMD) __u64 debug : 1,
14638918e0bSBorislav Petkov (AMD)
14738918e0bSBorislav Petkov (AMD) /*
14838918e0bSBorislav Petkov (AMD) * Indicates when the per-CPU GHCB has been created and registered
14938918e0bSBorislav Petkov (AMD) * and thus can be used by the BSP instead of the early boot GHCB.
15038918e0bSBorislav Petkov (AMD) *
15138918e0bSBorislav Petkov (AMD) * For APs, the per-CPU GHCB is created before they are started
15238918e0bSBorislav Petkov (AMD) * and registered upon startup, so this flag can be used globally
15338918e0bSBorislav Petkov (AMD) * for the BSP and APs.
15438918e0bSBorislav Petkov (AMD) */
15538918e0bSBorislav Petkov (AMD) ghcbs_initialized : 1,
15638918e0bSBorislav Petkov (AMD)
15738918e0bSBorislav Petkov (AMD) /*
15838918e0bSBorislav Petkov (AMD) * Indicates when the per-CPU SVSM CA is to be used instead of the
15938918e0bSBorislav Petkov (AMD) * boot SVSM CA.
16038918e0bSBorislav Petkov (AMD) *
16138918e0bSBorislav Petkov (AMD) * For APs, the per-CPU SVSM CA is created as part of the AP
16238918e0bSBorislav Petkov (AMD) * bringup, so this flag can be used globally for the BSP and APs.
16338918e0bSBorislav Petkov (AMD) */
16438918e0bSBorislav Petkov (AMD) use_cas : 1,
16538918e0bSBorislav Petkov (AMD)
166*c14e4114SPavan Kumar Paluri __reserved : 61;
16738918e0bSBorislav Petkov (AMD) };
16838918e0bSBorislav Petkov (AMD)
16938918e0bSBorislav Petkov (AMD) static struct sev_config sev_cfg __read_mostly;
17038918e0bSBorislav Petkov (AMD)
on_vc_stack(struct pt_regs * regs)17138918e0bSBorislav Petkov (AMD) static __always_inline bool on_vc_stack(struct pt_regs *regs)
17238918e0bSBorislav Petkov (AMD) {
17338918e0bSBorislav Petkov (AMD) unsigned long sp = regs->sp;
17438918e0bSBorislav Petkov (AMD)
17538918e0bSBorislav Petkov (AMD) /* User-mode RSP is not trusted */
17638918e0bSBorislav Petkov (AMD) if (user_mode(regs))
17738918e0bSBorislav Petkov (AMD) return false;
17838918e0bSBorislav Petkov (AMD)
17938918e0bSBorislav Petkov (AMD) /* SYSCALL gap still has user-mode RSP */
18038918e0bSBorislav Petkov (AMD) if (ip_within_syscall_gap(regs))
18138918e0bSBorislav Petkov (AMD) return false;
18238918e0bSBorislav Petkov (AMD)
18338918e0bSBorislav Petkov (AMD) return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC)));
18438918e0bSBorislav Petkov (AMD) }
18538918e0bSBorislav Petkov (AMD)
18638918e0bSBorislav Petkov (AMD) /*
18738918e0bSBorislav Petkov (AMD) * This function handles the case when an NMI is raised in the #VC
18838918e0bSBorislav Petkov (AMD) * exception handler entry code, before the #VC handler has switched off
18938918e0bSBorislav Petkov (AMD) * its IST stack. In this case, the IST entry for #VC must be adjusted,
19038918e0bSBorislav Petkov (AMD) * so that any nested #VC exception will not overwrite the stack
19138918e0bSBorislav Petkov (AMD) * contents of the interrupted #VC handler.
19238918e0bSBorislav Petkov (AMD) *
19338918e0bSBorislav Petkov (AMD) * The IST entry is adjusted unconditionally so that it can be also be
19438918e0bSBorislav Petkov (AMD) * unconditionally adjusted back in __sev_es_ist_exit(). Otherwise a
19538918e0bSBorislav Petkov (AMD) * nested sev_es_ist_exit() call may adjust back the IST entry too
19638918e0bSBorislav Petkov (AMD) * early.
19738918e0bSBorislav Petkov (AMD) *
19838918e0bSBorislav Petkov (AMD) * The __sev_es_ist_enter() and __sev_es_ist_exit() functions always run
19938918e0bSBorislav Petkov (AMD) * on the NMI IST stack, as they are only called from NMI handling code
20038918e0bSBorislav Petkov (AMD) * right now.
20138918e0bSBorislav Petkov (AMD) */
__sev_es_ist_enter(struct pt_regs * regs)20238918e0bSBorislav Petkov (AMD) void noinstr __sev_es_ist_enter(struct pt_regs *regs)
20338918e0bSBorislav Petkov (AMD) {
20438918e0bSBorislav Petkov (AMD) unsigned long old_ist, new_ist;
20538918e0bSBorislav Petkov (AMD)
20638918e0bSBorislav Petkov (AMD) /* Read old IST entry */
20738918e0bSBorislav Petkov (AMD) new_ist = old_ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]);
20838918e0bSBorislav Petkov (AMD)
20938918e0bSBorislav Petkov (AMD) /*
21038918e0bSBorislav Petkov (AMD) * If NMI happened while on the #VC IST stack, set the new IST
21138918e0bSBorislav Petkov (AMD) * value below regs->sp, so that the interrupted stack frame is
21238918e0bSBorislav Petkov (AMD) * not overwritten by subsequent #VC exceptions.
21338918e0bSBorislav Petkov (AMD) */
21438918e0bSBorislav Petkov (AMD) if (on_vc_stack(regs))
21538918e0bSBorislav Petkov (AMD) new_ist = regs->sp;
21638918e0bSBorislav Petkov (AMD)
21738918e0bSBorislav Petkov (AMD) /*
21838918e0bSBorislav Petkov (AMD) * Reserve additional 8 bytes and store old IST value so this
21938918e0bSBorislav Petkov (AMD) * adjustment can be unrolled in __sev_es_ist_exit().
22038918e0bSBorislav Petkov (AMD) */
22138918e0bSBorislav Petkov (AMD) new_ist -= sizeof(old_ist);
22238918e0bSBorislav Petkov (AMD) *(unsigned long *)new_ist = old_ist;
22338918e0bSBorislav Petkov (AMD)
22438918e0bSBorislav Petkov (AMD) /* Set new IST entry */
22538918e0bSBorislav Petkov (AMD) this_cpu_write(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC], new_ist);
22638918e0bSBorislav Petkov (AMD) }
22738918e0bSBorislav Petkov (AMD)
__sev_es_ist_exit(void)22838918e0bSBorislav Petkov (AMD) void noinstr __sev_es_ist_exit(void)
22938918e0bSBorislav Petkov (AMD) {
23038918e0bSBorislav Petkov (AMD) unsigned long ist;
23138918e0bSBorislav Petkov (AMD)
23238918e0bSBorislav Petkov (AMD) /* Read IST entry */
23338918e0bSBorislav Petkov (AMD) ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]);
23438918e0bSBorislav Petkov (AMD)
23538918e0bSBorislav Petkov (AMD) if (WARN_ON(ist == __this_cpu_ist_top_va(VC)))
23638918e0bSBorislav Petkov (AMD) return;
23738918e0bSBorislav Petkov (AMD)
23838918e0bSBorislav Petkov (AMD) /* Read back old IST entry and write it to the TSS */
23938918e0bSBorislav Petkov (AMD) this_cpu_write(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC], *(unsigned long *)ist);
24038918e0bSBorislav Petkov (AMD) }
24138918e0bSBorislav Petkov (AMD)
24238918e0bSBorislav Petkov (AMD) /*
24338918e0bSBorislav Petkov (AMD) * Nothing shall interrupt this code path while holding the per-CPU
24438918e0bSBorislav Petkov (AMD) * GHCB. The backup GHCB is only for NMIs interrupting this path.
24538918e0bSBorislav Petkov (AMD) *
24638918e0bSBorislav Petkov (AMD) * Callers must disable local interrupts around it.
24738918e0bSBorislav Petkov (AMD) */
__sev_get_ghcb(struct ghcb_state * state)24838918e0bSBorislav Petkov (AMD) static noinstr struct ghcb *__sev_get_ghcb(struct ghcb_state *state)
24938918e0bSBorislav Petkov (AMD) {
25038918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data;
25138918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
25238918e0bSBorislav Petkov (AMD)
25338918e0bSBorislav Petkov (AMD) WARN_ON(!irqs_disabled());
25438918e0bSBorislav Petkov (AMD)
25538918e0bSBorislav Petkov (AMD) data = this_cpu_read(runtime_data);
25638918e0bSBorislav Petkov (AMD) ghcb = &data->ghcb_page;
25738918e0bSBorislav Petkov (AMD)
25838918e0bSBorislav Petkov (AMD) if (unlikely(data->ghcb_active)) {
25938918e0bSBorislav Petkov (AMD) /* GHCB is already in use - save its contents */
26038918e0bSBorislav Petkov (AMD)
26138918e0bSBorislav Petkov (AMD) if (unlikely(data->backup_ghcb_active)) {
26238918e0bSBorislav Petkov (AMD) /*
26338918e0bSBorislav Petkov (AMD) * Backup-GHCB is also already in use. There is no way
26438918e0bSBorislav Petkov (AMD) * to continue here so just kill the machine. To make
26538918e0bSBorislav Petkov (AMD) * panic() work, mark GHCBs inactive so that messages
26638918e0bSBorislav Petkov (AMD) * can be printed out.
26738918e0bSBorislav Petkov (AMD) */
26838918e0bSBorislav Petkov (AMD) data->ghcb_active = false;
26938918e0bSBorislav Petkov (AMD) data->backup_ghcb_active = false;
27038918e0bSBorislav Petkov (AMD)
27138918e0bSBorislav Petkov (AMD) instrumentation_begin();
27238918e0bSBorislav Petkov (AMD) panic("Unable to handle #VC exception! GHCB and Backup GHCB are already in use");
27338918e0bSBorislav Petkov (AMD) instrumentation_end();
27438918e0bSBorislav Petkov (AMD) }
27538918e0bSBorislav Petkov (AMD)
27638918e0bSBorislav Petkov (AMD) /* Mark backup_ghcb active before writing to it */
27738918e0bSBorislav Petkov (AMD) data->backup_ghcb_active = true;
27838918e0bSBorislav Petkov (AMD)
27938918e0bSBorislav Petkov (AMD) state->ghcb = &data->backup_ghcb;
28038918e0bSBorislav Petkov (AMD)
28138918e0bSBorislav Petkov (AMD) /* Backup GHCB content */
28238918e0bSBorislav Petkov (AMD) *state->ghcb = *ghcb;
28338918e0bSBorislav Petkov (AMD) } else {
28438918e0bSBorislav Petkov (AMD) state->ghcb = NULL;
28538918e0bSBorislav Petkov (AMD) data->ghcb_active = true;
28638918e0bSBorislav Petkov (AMD) }
28738918e0bSBorislav Petkov (AMD)
28838918e0bSBorislav Petkov (AMD) return ghcb;
28938918e0bSBorislav Petkov (AMD) }
29038918e0bSBorislav Petkov (AMD)
sev_es_rd_ghcb_msr(void)29138918e0bSBorislav Petkov (AMD) static inline u64 sev_es_rd_ghcb_msr(void)
29238918e0bSBorislav Petkov (AMD) {
29338918e0bSBorislav Petkov (AMD) return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
29438918e0bSBorislav Petkov (AMD) }
29538918e0bSBorislav Petkov (AMD)
sev_es_wr_ghcb_msr(u64 val)29638918e0bSBorislav Petkov (AMD) static __always_inline void sev_es_wr_ghcb_msr(u64 val)
29738918e0bSBorislav Petkov (AMD) {
29838918e0bSBorislav Petkov (AMD) u32 low, high;
29938918e0bSBorislav Petkov (AMD)
30038918e0bSBorislav Petkov (AMD) low = (u32)(val);
30138918e0bSBorislav Petkov (AMD) high = (u32)(val >> 32);
30238918e0bSBorislav Petkov (AMD)
30338918e0bSBorislav Petkov (AMD) native_wrmsr(MSR_AMD64_SEV_ES_GHCB, low, high);
30438918e0bSBorislav Petkov (AMD) }
30538918e0bSBorislav Petkov (AMD)
vc_fetch_insn_kernel(struct es_em_ctxt * ctxt,unsigned char * buffer)30638918e0bSBorislav Petkov (AMD) static int vc_fetch_insn_kernel(struct es_em_ctxt *ctxt,
30738918e0bSBorislav Petkov (AMD) unsigned char *buffer)
30838918e0bSBorislav Petkov (AMD) {
30938918e0bSBorislav Petkov (AMD) return copy_from_kernel_nofault(buffer, (unsigned char *)ctxt->regs->ip, MAX_INSN_SIZE);
31038918e0bSBorislav Petkov (AMD) }
31138918e0bSBorislav Petkov (AMD)
__vc_decode_user_insn(struct es_em_ctxt * ctxt)31238918e0bSBorislav Petkov (AMD) static enum es_result __vc_decode_user_insn(struct es_em_ctxt *ctxt)
31338918e0bSBorislav Petkov (AMD) {
31438918e0bSBorislav Petkov (AMD) char buffer[MAX_INSN_SIZE];
31538918e0bSBorislav Petkov (AMD) int insn_bytes;
31638918e0bSBorislav Petkov (AMD)
31738918e0bSBorislav Petkov (AMD) insn_bytes = insn_fetch_from_user_inatomic(ctxt->regs, buffer);
31838918e0bSBorislav Petkov (AMD) if (insn_bytes == 0) {
31938918e0bSBorislav Petkov (AMD) /* Nothing could be copied */
32038918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_PF;
32138918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = X86_PF_INSTR | X86_PF_USER;
32238918e0bSBorislav Petkov (AMD) ctxt->fi.cr2 = ctxt->regs->ip;
32338918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
32438918e0bSBorislav Petkov (AMD) } else if (insn_bytes == -EINVAL) {
32538918e0bSBorislav Petkov (AMD) /* Effective RIP could not be calculated */
32638918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_GP;
32738918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = 0;
32838918e0bSBorislav Petkov (AMD) ctxt->fi.cr2 = 0;
32938918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
33038918e0bSBorislav Petkov (AMD) }
33138918e0bSBorislav Petkov (AMD)
33238918e0bSBorislav Petkov (AMD) if (!insn_decode_from_regs(&ctxt->insn, ctxt->regs, buffer, insn_bytes))
33338918e0bSBorislav Petkov (AMD) return ES_DECODE_FAILED;
33438918e0bSBorislav Petkov (AMD)
33538918e0bSBorislav Petkov (AMD) if (ctxt->insn.immediate.got)
33638918e0bSBorislav Petkov (AMD) return ES_OK;
33738918e0bSBorislav Petkov (AMD) else
33838918e0bSBorislav Petkov (AMD) return ES_DECODE_FAILED;
33938918e0bSBorislav Petkov (AMD) }
34038918e0bSBorislav Petkov (AMD)
__vc_decode_kern_insn(struct es_em_ctxt * ctxt)34138918e0bSBorislav Petkov (AMD) static enum es_result __vc_decode_kern_insn(struct es_em_ctxt *ctxt)
34238918e0bSBorislav Petkov (AMD) {
34338918e0bSBorislav Petkov (AMD) char buffer[MAX_INSN_SIZE];
34438918e0bSBorislav Petkov (AMD) int res, ret;
34538918e0bSBorislav Petkov (AMD)
34638918e0bSBorislav Petkov (AMD) res = vc_fetch_insn_kernel(ctxt, buffer);
34738918e0bSBorislav Petkov (AMD) if (res) {
34838918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_PF;
34938918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = X86_PF_INSTR;
35038918e0bSBorislav Petkov (AMD) ctxt->fi.cr2 = ctxt->regs->ip;
35138918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
35238918e0bSBorislav Petkov (AMD) }
35338918e0bSBorislav Petkov (AMD)
35438918e0bSBorislav Petkov (AMD) ret = insn_decode(&ctxt->insn, buffer, MAX_INSN_SIZE, INSN_MODE_64);
35538918e0bSBorislav Petkov (AMD) if (ret < 0)
35638918e0bSBorislav Petkov (AMD) return ES_DECODE_FAILED;
35738918e0bSBorislav Petkov (AMD) else
35838918e0bSBorislav Petkov (AMD) return ES_OK;
35938918e0bSBorislav Petkov (AMD) }
36038918e0bSBorislav Petkov (AMD)
vc_decode_insn(struct es_em_ctxt * ctxt)36138918e0bSBorislav Petkov (AMD) static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
36238918e0bSBorislav Petkov (AMD) {
36338918e0bSBorislav Petkov (AMD) if (user_mode(ctxt->regs))
36438918e0bSBorislav Petkov (AMD) return __vc_decode_user_insn(ctxt);
36538918e0bSBorislav Petkov (AMD) else
36638918e0bSBorislav Petkov (AMD) return __vc_decode_kern_insn(ctxt);
36738918e0bSBorislav Petkov (AMD) }
36838918e0bSBorislav Petkov (AMD)
vc_write_mem(struct es_em_ctxt * ctxt,char * dst,char * buf,size_t size)36938918e0bSBorislav Petkov (AMD) static enum es_result vc_write_mem(struct es_em_ctxt *ctxt,
37038918e0bSBorislav Petkov (AMD) char *dst, char *buf, size_t size)
37138918e0bSBorislav Petkov (AMD) {
37238918e0bSBorislav Petkov (AMD) unsigned long error_code = X86_PF_PROT | X86_PF_WRITE;
37338918e0bSBorislav Petkov (AMD)
37438918e0bSBorislav Petkov (AMD) /*
37538918e0bSBorislav Petkov (AMD) * This function uses __put_user() independent of whether kernel or user
37638918e0bSBorislav Petkov (AMD) * memory is accessed. This works fine because __put_user() does no
37738918e0bSBorislav Petkov (AMD) * sanity checks of the pointer being accessed. All that it does is
37838918e0bSBorislav Petkov (AMD) * to report when the access failed.
37938918e0bSBorislav Petkov (AMD) *
38038918e0bSBorislav Petkov (AMD) * Also, this function runs in atomic context, so __put_user() is not
38138918e0bSBorislav Petkov (AMD) * allowed to sleep. The page-fault handler detects that it is running
38238918e0bSBorislav Petkov (AMD) * in atomic context and will not try to take mmap_sem and handle the
38338918e0bSBorislav Petkov (AMD) * fault, so additional pagefault_enable()/disable() calls are not
38438918e0bSBorislav Petkov (AMD) * needed.
38538918e0bSBorislav Petkov (AMD) *
38638918e0bSBorislav Petkov (AMD) * The access can't be done via copy_to_user() here because
38738918e0bSBorislav Petkov (AMD) * vc_write_mem() must not use string instructions to access unsafe
38838918e0bSBorislav Petkov (AMD) * memory. The reason is that MOVS is emulated by the #VC handler by
38938918e0bSBorislav Petkov (AMD) * splitting the move up into a read and a write and taking a nested #VC
39038918e0bSBorislav Petkov (AMD) * exception on whatever of them is the MMIO access. Using string
39138918e0bSBorislav Petkov (AMD) * instructions here would cause infinite nesting.
39238918e0bSBorislav Petkov (AMD) */
39338918e0bSBorislav Petkov (AMD) switch (size) {
39438918e0bSBorislav Petkov (AMD) case 1: {
39538918e0bSBorislav Petkov (AMD) u8 d1;
39638918e0bSBorislav Petkov (AMD) u8 __user *target = (u8 __user *)dst;
39738918e0bSBorislav Petkov (AMD)
39838918e0bSBorislav Petkov (AMD) memcpy(&d1, buf, 1);
39938918e0bSBorislav Petkov (AMD) if (__put_user(d1, target))
40038918e0bSBorislav Petkov (AMD) goto fault;
40138918e0bSBorislav Petkov (AMD) break;
40238918e0bSBorislav Petkov (AMD) }
40338918e0bSBorislav Petkov (AMD) case 2: {
40438918e0bSBorislav Petkov (AMD) u16 d2;
40538918e0bSBorislav Petkov (AMD) u16 __user *target = (u16 __user *)dst;
40638918e0bSBorislav Petkov (AMD)
40738918e0bSBorislav Petkov (AMD) memcpy(&d2, buf, 2);
40838918e0bSBorislav Petkov (AMD) if (__put_user(d2, target))
40938918e0bSBorislav Petkov (AMD) goto fault;
41038918e0bSBorislav Petkov (AMD) break;
41138918e0bSBorislav Petkov (AMD) }
41238918e0bSBorislav Petkov (AMD) case 4: {
41338918e0bSBorislav Petkov (AMD) u32 d4;
41438918e0bSBorislav Petkov (AMD) u32 __user *target = (u32 __user *)dst;
41538918e0bSBorislav Petkov (AMD)
41638918e0bSBorislav Petkov (AMD) memcpy(&d4, buf, 4);
41738918e0bSBorislav Petkov (AMD) if (__put_user(d4, target))
41838918e0bSBorislav Petkov (AMD) goto fault;
41938918e0bSBorislav Petkov (AMD) break;
42038918e0bSBorislav Petkov (AMD) }
42138918e0bSBorislav Petkov (AMD) case 8: {
42238918e0bSBorislav Petkov (AMD) u64 d8;
42338918e0bSBorislav Petkov (AMD) u64 __user *target = (u64 __user *)dst;
42438918e0bSBorislav Petkov (AMD)
42538918e0bSBorislav Petkov (AMD) memcpy(&d8, buf, 8);
42638918e0bSBorislav Petkov (AMD) if (__put_user(d8, target))
42738918e0bSBorislav Petkov (AMD) goto fault;
42838918e0bSBorislav Petkov (AMD) break;
42938918e0bSBorislav Petkov (AMD) }
43038918e0bSBorislav Petkov (AMD) default:
43138918e0bSBorislav Petkov (AMD) WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size);
43238918e0bSBorislav Petkov (AMD) return ES_UNSUPPORTED;
43338918e0bSBorislav Petkov (AMD) }
43438918e0bSBorislav Petkov (AMD)
43538918e0bSBorislav Petkov (AMD) return ES_OK;
43638918e0bSBorislav Petkov (AMD)
43738918e0bSBorislav Petkov (AMD) fault:
43838918e0bSBorislav Petkov (AMD) if (user_mode(ctxt->regs))
43938918e0bSBorislav Petkov (AMD) error_code |= X86_PF_USER;
44038918e0bSBorislav Petkov (AMD)
44138918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_PF;
44238918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = error_code;
44338918e0bSBorislav Petkov (AMD) ctxt->fi.cr2 = (unsigned long)dst;
44438918e0bSBorislav Petkov (AMD)
44538918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
44638918e0bSBorislav Petkov (AMD) }
44738918e0bSBorislav Petkov (AMD)
vc_read_mem(struct es_em_ctxt * ctxt,char * src,char * buf,size_t size)44838918e0bSBorislav Petkov (AMD) static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
44938918e0bSBorislav Petkov (AMD) char *src, char *buf, size_t size)
45038918e0bSBorislav Petkov (AMD) {
45138918e0bSBorislav Petkov (AMD) unsigned long error_code = X86_PF_PROT;
45238918e0bSBorislav Petkov (AMD)
45338918e0bSBorislav Petkov (AMD) /*
45438918e0bSBorislav Petkov (AMD) * This function uses __get_user() independent of whether kernel or user
45538918e0bSBorislav Petkov (AMD) * memory is accessed. This works fine because __get_user() does no
45638918e0bSBorislav Petkov (AMD) * sanity checks of the pointer being accessed. All that it does is
45738918e0bSBorislav Petkov (AMD) * to report when the access failed.
45838918e0bSBorislav Petkov (AMD) *
45938918e0bSBorislav Petkov (AMD) * Also, this function runs in atomic context, so __get_user() is not
46038918e0bSBorislav Petkov (AMD) * allowed to sleep. The page-fault handler detects that it is running
46138918e0bSBorislav Petkov (AMD) * in atomic context and will not try to take mmap_sem and handle the
46238918e0bSBorislav Petkov (AMD) * fault, so additional pagefault_enable()/disable() calls are not
46338918e0bSBorislav Petkov (AMD) * needed.
46438918e0bSBorislav Petkov (AMD) *
46538918e0bSBorislav Petkov (AMD) * The access can't be done via copy_from_user() here because
46638918e0bSBorislav Petkov (AMD) * vc_read_mem() must not use string instructions to access unsafe
46738918e0bSBorislav Petkov (AMD) * memory. The reason is that MOVS is emulated by the #VC handler by
46838918e0bSBorislav Petkov (AMD) * splitting the move up into a read and a write and taking a nested #VC
46938918e0bSBorislav Petkov (AMD) * exception on whatever of them is the MMIO access. Using string
47038918e0bSBorislav Petkov (AMD) * instructions here would cause infinite nesting.
47138918e0bSBorislav Petkov (AMD) */
47238918e0bSBorislav Petkov (AMD) switch (size) {
47338918e0bSBorislav Petkov (AMD) case 1: {
47438918e0bSBorislav Petkov (AMD) u8 d1;
47538918e0bSBorislav Petkov (AMD) u8 __user *s = (u8 __user *)src;
47638918e0bSBorislav Petkov (AMD)
47738918e0bSBorislav Petkov (AMD) if (__get_user(d1, s))
47838918e0bSBorislav Petkov (AMD) goto fault;
47938918e0bSBorislav Petkov (AMD) memcpy(buf, &d1, 1);
48038918e0bSBorislav Petkov (AMD) break;
48138918e0bSBorislav Petkov (AMD) }
48238918e0bSBorislav Petkov (AMD) case 2: {
48338918e0bSBorislav Petkov (AMD) u16 d2;
48438918e0bSBorislav Petkov (AMD) u16 __user *s = (u16 __user *)src;
48538918e0bSBorislav Petkov (AMD)
48638918e0bSBorislav Petkov (AMD) if (__get_user(d2, s))
48738918e0bSBorislav Petkov (AMD) goto fault;
48838918e0bSBorislav Petkov (AMD) memcpy(buf, &d2, 2);
48938918e0bSBorislav Petkov (AMD) break;
49038918e0bSBorislav Petkov (AMD) }
49138918e0bSBorislav Petkov (AMD) case 4: {
49238918e0bSBorislav Petkov (AMD) u32 d4;
49338918e0bSBorislav Petkov (AMD) u32 __user *s = (u32 __user *)src;
49438918e0bSBorislav Petkov (AMD)
49538918e0bSBorislav Petkov (AMD) if (__get_user(d4, s))
49638918e0bSBorislav Petkov (AMD) goto fault;
49738918e0bSBorislav Petkov (AMD) memcpy(buf, &d4, 4);
49838918e0bSBorislav Petkov (AMD) break;
49938918e0bSBorislav Petkov (AMD) }
50038918e0bSBorislav Petkov (AMD) case 8: {
50138918e0bSBorislav Petkov (AMD) u64 d8;
50238918e0bSBorislav Petkov (AMD) u64 __user *s = (u64 __user *)src;
50338918e0bSBorislav Petkov (AMD) if (__get_user(d8, s))
50438918e0bSBorislav Petkov (AMD) goto fault;
50538918e0bSBorislav Petkov (AMD) memcpy(buf, &d8, 8);
50638918e0bSBorislav Petkov (AMD) break;
50738918e0bSBorislav Petkov (AMD) }
50838918e0bSBorislav Petkov (AMD) default:
50938918e0bSBorislav Petkov (AMD) WARN_ONCE(1, "%s: Invalid size: %zu\n", __func__, size);
51038918e0bSBorislav Petkov (AMD) return ES_UNSUPPORTED;
51138918e0bSBorislav Petkov (AMD) }
51238918e0bSBorislav Petkov (AMD)
51338918e0bSBorislav Petkov (AMD) return ES_OK;
51438918e0bSBorislav Petkov (AMD)
51538918e0bSBorislav Petkov (AMD) fault:
51638918e0bSBorislav Petkov (AMD) if (user_mode(ctxt->regs))
51738918e0bSBorislav Petkov (AMD) error_code |= X86_PF_USER;
51838918e0bSBorislav Petkov (AMD)
51938918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_PF;
52038918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = error_code;
52138918e0bSBorislav Petkov (AMD) ctxt->fi.cr2 = (unsigned long)src;
52238918e0bSBorislav Petkov (AMD)
52338918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
52438918e0bSBorislav Petkov (AMD) }
52538918e0bSBorislav Petkov (AMD)
vc_slow_virt_to_phys(struct ghcb * ghcb,struct es_em_ctxt * ctxt,unsigned long vaddr,phys_addr_t * paddr)52638918e0bSBorislav Petkov (AMD) static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt *ctxt,
52738918e0bSBorislav Petkov (AMD) unsigned long vaddr, phys_addr_t *paddr)
52838918e0bSBorislav Petkov (AMD) {
52938918e0bSBorislav Petkov (AMD) unsigned long va = (unsigned long)vaddr;
53038918e0bSBorislav Petkov (AMD) unsigned int level;
53138918e0bSBorislav Petkov (AMD) phys_addr_t pa;
53238918e0bSBorislav Petkov (AMD) pgd_t *pgd;
53338918e0bSBorislav Petkov (AMD) pte_t *pte;
53438918e0bSBorislav Petkov (AMD)
53538918e0bSBorislav Petkov (AMD) pgd = __va(read_cr3_pa());
53638918e0bSBorislav Petkov (AMD) pgd = &pgd[pgd_index(va)];
53738918e0bSBorislav Petkov (AMD) pte = lookup_address_in_pgd(pgd, va, &level);
53838918e0bSBorislav Petkov (AMD) if (!pte) {
53938918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_PF;
54038918e0bSBorislav Petkov (AMD) ctxt->fi.cr2 = vaddr;
54138918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = 0;
54238918e0bSBorislav Petkov (AMD)
54338918e0bSBorislav Petkov (AMD) if (user_mode(ctxt->regs))
54438918e0bSBorislav Petkov (AMD) ctxt->fi.error_code |= X86_PF_USER;
54538918e0bSBorislav Petkov (AMD)
54638918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
54738918e0bSBorislav Petkov (AMD) }
54838918e0bSBorislav Petkov (AMD)
54938918e0bSBorislav Petkov (AMD) if (WARN_ON_ONCE(pte_val(*pte) & _PAGE_ENC))
55038918e0bSBorislav Petkov (AMD) /* Emulated MMIO to/from encrypted memory not supported */
55138918e0bSBorislav Petkov (AMD) return ES_UNSUPPORTED;
55238918e0bSBorislav Petkov (AMD)
55338918e0bSBorislav Petkov (AMD) pa = (phys_addr_t)pte_pfn(*pte) << PAGE_SHIFT;
55438918e0bSBorislav Petkov (AMD) pa |= va & ~page_level_mask(level);
55538918e0bSBorislav Petkov (AMD)
55638918e0bSBorislav Petkov (AMD) *paddr = pa;
55738918e0bSBorislav Petkov (AMD)
55838918e0bSBorislav Petkov (AMD) return ES_OK;
55938918e0bSBorislav Petkov (AMD) }
56038918e0bSBorislav Petkov (AMD)
vc_ioio_check(struct es_em_ctxt * ctxt,u16 port,size_t size)56138918e0bSBorislav Petkov (AMD) static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
56238918e0bSBorislav Petkov (AMD) {
56338918e0bSBorislav Petkov (AMD) BUG_ON(size > 4);
56438918e0bSBorislav Petkov (AMD)
56538918e0bSBorislav Petkov (AMD) if (user_mode(ctxt->regs)) {
56638918e0bSBorislav Petkov (AMD) struct thread_struct *t = ¤t->thread;
56738918e0bSBorislav Petkov (AMD) struct io_bitmap *iobm = t->io_bitmap;
56838918e0bSBorislav Petkov (AMD) size_t idx;
56938918e0bSBorislav Petkov (AMD)
57038918e0bSBorislav Petkov (AMD) if (!iobm)
57138918e0bSBorislav Petkov (AMD) goto fault;
57238918e0bSBorislav Petkov (AMD)
57338918e0bSBorislav Petkov (AMD) for (idx = port; idx < port + size; ++idx) {
57438918e0bSBorislav Petkov (AMD) if (test_bit(idx, iobm->bitmap))
57538918e0bSBorislav Petkov (AMD) goto fault;
57638918e0bSBorislav Petkov (AMD) }
57738918e0bSBorislav Petkov (AMD) }
57838918e0bSBorislav Petkov (AMD)
57938918e0bSBorislav Petkov (AMD) return ES_OK;
58038918e0bSBorislav Petkov (AMD)
58138918e0bSBorislav Petkov (AMD) fault:
58238918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_GP;
58338918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = 0;
58438918e0bSBorislav Petkov (AMD)
58538918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
58638918e0bSBorislav Petkov (AMD) }
58738918e0bSBorislav Petkov (AMD)
vc_forward_exception(struct es_em_ctxt * ctxt)58838918e0bSBorislav Petkov (AMD) static __always_inline void vc_forward_exception(struct es_em_ctxt *ctxt)
58938918e0bSBorislav Petkov (AMD) {
59038918e0bSBorislav Petkov (AMD) long error_code = ctxt->fi.error_code;
59138918e0bSBorislav Petkov (AMD) int trapnr = ctxt->fi.vector;
59238918e0bSBorislav Petkov (AMD)
59338918e0bSBorislav Petkov (AMD) ctxt->regs->orig_ax = ctxt->fi.error_code;
59438918e0bSBorislav Petkov (AMD)
59538918e0bSBorislav Petkov (AMD) switch (trapnr) {
59638918e0bSBorislav Petkov (AMD) case X86_TRAP_GP:
59738918e0bSBorislav Petkov (AMD) exc_general_protection(ctxt->regs, error_code);
59838918e0bSBorislav Petkov (AMD) break;
59938918e0bSBorislav Petkov (AMD) case X86_TRAP_UD:
60038918e0bSBorislav Petkov (AMD) exc_invalid_op(ctxt->regs);
60138918e0bSBorislav Petkov (AMD) break;
60238918e0bSBorislav Petkov (AMD) case X86_TRAP_PF:
60338918e0bSBorislav Petkov (AMD) write_cr2(ctxt->fi.cr2);
60438918e0bSBorislav Petkov (AMD) exc_page_fault(ctxt->regs, error_code);
60538918e0bSBorislav Petkov (AMD) break;
60638918e0bSBorislav Petkov (AMD) case X86_TRAP_AC:
60738918e0bSBorislav Petkov (AMD) exc_alignment_check(ctxt->regs, error_code);
60838918e0bSBorislav Petkov (AMD) break;
60938918e0bSBorislav Petkov (AMD) default:
61038918e0bSBorislav Petkov (AMD) pr_emerg("Unsupported exception in #VC instruction emulation - can't continue\n");
61138918e0bSBorislav Petkov (AMD) BUG();
61238918e0bSBorislav Petkov (AMD) }
61338918e0bSBorislav Petkov (AMD) }
61438918e0bSBorislav Petkov (AMD)
61538918e0bSBorislav Petkov (AMD) /* Include code shared with pre-decompression boot stage */
61638918e0bSBorislav Petkov (AMD) #include "shared.c"
61738918e0bSBorislav Petkov (AMD)
svsm_get_caa(void)61838918e0bSBorislav Petkov (AMD) static inline struct svsm_ca *svsm_get_caa(void)
61938918e0bSBorislav Petkov (AMD) {
62038918e0bSBorislav Petkov (AMD) /*
62138918e0bSBorislav Petkov (AMD) * Use rIP-relative references when called early in the boot. If
62238918e0bSBorislav Petkov (AMD) * ->use_cas is set, then it is late in the boot and no need
62338918e0bSBorislav Petkov (AMD) * to worry about rIP-relative references.
62438918e0bSBorislav Petkov (AMD) */
62538918e0bSBorislav Petkov (AMD) if (RIP_REL_REF(sev_cfg).use_cas)
62638918e0bSBorislav Petkov (AMD) return this_cpu_read(svsm_caa);
62738918e0bSBorislav Petkov (AMD) else
62838918e0bSBorislav Petkov (AMD) return RIP_REL_REF(boot_svsm_caa);
62938918e0bSBorislav Petkov (AMD) }
63038918e0bSBorislav Petkov (AMD)
svsm_get_caa_pa(void)63138918e0bSBorislav Petkov (AMD) static u64 svsm_get_caa_pa(void)
63238918e0bSBorislav Petkov (AMD) {
63338918e0bSBorislav Petkov (AMD) /*
63438918e0bSBorislav Petkov (AMD) * Use rIP-relative references when called early in the boot. If
63538918e0bSBorislav Petkov (AMD) * ->use_cas is set, then it is late in the boot and no need
63638918e0bSBorislav Petkov (AMD) * to worry about rIP-relative references.
63738918e0bSBorislav Petkov (AMD) */
63838918e0bSBorislav Petkov (AMD) if (RIP_REL_REF(sev_cfg).use_cas)
63938918e0bSBorislav Petkov (AMD) return this_cpu_read(svsm_caa_pa);
64038918e0bSBorislav Petkov (AMD) else
64138918e0bSBorislav Petkov (AMD) return RIP_REL_REF(boot_svsm_caa_pa);
64238918e0bSBorislav Petkov (AMD) }
64338918e0bSBorislav Petkov (AMD)
__sev_put_ghcb(struct ghcb_state * state)64438918e0bSBorislav Petkov (AMD) static noinstr void __sev_put_ghcb(struct ghcb_state *state)
64538918e0bSBorislav Petkov (AMD) {
64638918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data;
64738918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
64838918e0bSBorislav Petkov (AMD)
64938918e0bSBorislav Petkov (AMD) WARN_ON(!irqs_disabled());
65038918e0bSBorislav Petkov (AMD)
65138918e0bSBorislav Petkov (AMD) data = this_cpu_read(runtime_data);
65238918e0bSBorislav Petkov (AMD) ghcb = &data->ghcb_page;
65338918e0bSBorislav Petkov (AMD)
65438918e0bSBorislav Petkov (AMD) if (state->ghcb) {
65538918e0bSBorislav Petkov (AMD) /* Restore GHCB from Backup */
65638918e0bSBorislav Petkov (AMD) *ghcb = *state->ghcb;
65738918e0bSBorislav Petkov (AMD) data->backup_ghcb_active = false;
65838918e0bSBorislav Petkov (AMD) state->ghcb = NULL;
65938918e0bSBorislav Petkov (AMD) } else {
66038918e0bSBorislav Petkov (AMD) /*
66138918e0bSBorislav Petkov (AMD) * Invalidate the GHCB so a VMGEXIT instruction issued
66238918e0bSBorislav Petkov (AMD) * from userspace won't appear to be valid.
66338918e0bSBorislav Petkov (AMD) */
66438918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(ghcb);
66538918e0bSBorislav Petkov (AMD) data->ghcb_active = false;
66638918e0bSBorislav Petkov (AMD) }
66738918e0bSBorislav Petkov (AMD) }
66838918e0bSBorislav Petkov (AMD)
svsm_perform_call_protocol(struct svsm_call * call)66938918e0bSBorislav Petkov (AMD) static int svsm_perform_call_protocol(struct svsm_call *call)
67038918e0bSBorislav Petkov (AMD) {
67138918e0bSBorislav Petkov (AMD) struct ghcb_state state;
67238918e0bSBorislav Petkov (AMD) unsigned long flags;
67338918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
67438918e0bSBorislav Petkov (AMD) int ret;
67538918e0bSBorislav Petkov (AMD)
67638918e0bSBorislav Petkov (AMD) /*
67738918e0bSBorislav Petkov (AMD) * This can be called very early in the boot, use native functions in
67838918e0bSBorislav Petkov (AMD) * order to avoid paravirt issues.
67938918e0bSBorislav Petkov (AMD) */
68038918e0bSBorislav Petkov (AMD) flags = native_local_irq_save();
68138918e0bSBorislav Petkov (AMD)
68238918e0bSBorislav Petkov (AMD) /*
68338918e0bSBorislav Petkov (AMD) * Use rip-relative references when called early in the boot. If
68438918e0bSBorislav Petkov (AMD) * ghcbs_initialized is set, then it is late in the boot and no need
68538918e0bSBorislav Petkov (AMD) * to worry about rip-relative references in called functions.
68638918e0bSBorislav Petkov (AMD) */
68738918e0bSBorislav Petkov (AMD) if (RIP_REL_REF(sev_cfg).ghcbs_initialized)
68838918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
68938918e0bSBorislav Petkov (AMD) else if (RIP_REL_REF(boot_ghcb))
69038918e0bSBorislav Petkov (AMD) ghcb = RIP_REL_REF(boot_ghcb);
69138918e0bSBorislav Petkov (AMD) else
69238918e0bSBorislav Petkov (AMD) ghcb = NULL;
69338918e0bSBorislav Petkov (AMD)
69438918e0bSBorislav Petkov (AMD) do {
69538918e0bSBorislav Petkov (AMD) ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call)
69638918e0bSBorislav Petkov (AMD) : svsm_perform_msr_protocol(call);
69738918e0bSBorislav Petkov (AMD) } while (ret == -EAGAIN);
69838918e0bSBorislav Petkov (AMD)
69938918e0bSBorislav Petkov (AMD) if (RIP_REL_REF(sev_cfg).ghcbs_initialized)
70038918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
70138918e0bSBorislav Petkov (AMD)
70238918e0bSBorislav Petkov (AMD) native_local_irq_restore(flags);
70338918e0bSBorislav Petkov (AMD)
70438918e0bSBorislav Petkov (AMD) return ret;
70538918e0bSBorislav Petkov (AMD) }
70638918e0bSBorislav Petkov (AMD)
__sev_es_nmi_complete(void)70738918e0bSBorislav Petkov (AMD) void noinstr __sev_es_nmi_complete(void)
70838918e0bSBorislav Petkov (AMD) {
70938918e0bSBorislav Petkov (AMD) struct ghcb_state state;
71038918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
71138918e0bSBorislav Petkov (AMD)
71238918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
71338918e0bSBorislav Petkov (AMD)
71438918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(ghcb);
71538918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_NMI_COMPLETE);
71638918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_1(ghcb, 0);
71738918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_2(ghcb, 0);
71838918e0bSBorislav Petkov (AMD)
71938918e0bSBorislav Petkov (AMD) sev_es_wr_ghcb_msr(__pa_nodebug(ghcb));
72038918e0bSBorislav Petkov (AMD) VMGEXIT();
72138918e0bSBorislav Petkov (AMD)
72238918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
72338918e0bSBorislav Petkov (AMD) }
72438918e0bSBorislav Petkov (AMD)
get_secrets_page(void)72538918e0bSBorislav Petkov (AMD) static u64 __init get_secrets_page(void)
72638918e0bSBorislav Petkov (AMD) {
72738918e0bSBorislav Petkov (AMD) u64 pa_data = boot_params.cc_blob_address;
72838918e0bSBorislav Petkov (AMD) struct cc_blob_sev_info info;
72938918e0bSBorislav Petkov (AMD) void *map;
73038918e0bSBorislav Petkov (AMD)
73138918e0bSBorislav Petkov (AMD) /*
73238918e0bSBorislav Petkov (AMD) * The CC blob contains the address of the secrets page, check if the
73338918e0bSBorislav Petkov (AMD) * blob is present.
73438918e0bSBorislav Petkov (AMD) */
73538918e0bSBorislav Petkov (AMD) if (!pa_data)
73638918e0bSBorislav Petkov (AMD) return 0;
73738918e0bSBorislav Petkov (AMD)
73838918e0bSBorislav Petkov (AMD) map = early_memremap(pa_data, sizeof(info));
73938918e0bSBorislav Petkov (AMD) if (!map) {
74038918e0bSBorislav Petkov (AMD) pr_err("Unable to locate SNP secrets page: failed to map the Confidential Computing blob.\n");
74138918e0bSBorislav Petkov (AMD) return 0;
74238918e0bSBorislav Petkov (AMD) }
74338918e0bSBorislav Petkov (AMD) memcpy(&info, map, sizeof(info));
74438918e0bSBorislav Petkov (AMD) early_memunmap(map, sizeof(info));
74538918e0bSBorislav Petkov (AMD)
74638918e0bSBorislav Petkov (AMD) /* smoke-test the secrets page passed */
74738918e0bSBorislav Petkov (AMD) if (!info.secrets_phys || info.secrets_len != PAGE_SIZE)
74838918e0bSBorislav Petkov (AMD) return 0;
74938918e0bSBorislav Petkov (AMD)
75038918e0bSBorislav Petkov (AMD) return info.secrets_phys;
75138918e0bSBorislav Petkov (AMD) }
75238918e0bSBorislav Petkov (AMD)
get_snp_jump_table_addr(void)75338918e0bSBorislav Petkov (AMD) static u64 __init get_snp_jump_table_addr(void)
75438918e0bSBorislav Petkov (AMD) {
75538918e0bSBorislav Petkov (AMD) struct snp_secrets_page *secrets;
75638918e0bSBorislav Petkov (AMD) void __iomem *mem;
75738918e0bSBorislav Petkov (AMD) u64 pa, addr;
75838918e0bSBorislav Petkov (AMD)
75938918e0bSBorislav Petkov (AMD) pa = get_secrets_page();
76038918e0bSBorislav Petkov (AMD) if (!pa)
76138918e0bSBorislav Petkov (AMD) return 0;
76238918e0bSBorislav Petkov (AMD)
76338918e0bSBorislav Petkov (AMD) mem = ioremap_encrypted(pa, PAGE_SIZE);
76438918e0bSBorislav Petkov (AMD) if (!mem) {
76538918e0bSBorislav Petkov (AMD) pr_err("Unable to locate AP jump table address: failed to map the SNP secrets page.\n");
76638918e0bSBorislav Petkov (AMD) return 0;
76738918e0bSBorislav Petkov (AMD) }
76838918e0bSBorislav Petkov (AMD)
76938918e0bSBorislav Petkov (AMD) secrets = (__force struct snp_secrets_page *)mem;
77038918e0bSBorislav Petkov (AMD)
77138918e0bSBorislav Petkov (AMD) addr = secrets->os_area.ap_jump_table_pa;
77238918e0bSBorislav Petkov (AMD) iounmap(mem);
77338918e0bSBorislav Petkov (AMD)
77438918e0bSBorislav Petkov (AMD) return addr;
77538918e0bSBorislav Petkov (AMD) }
77638918e0bSBorislav Petkov (AMD)
get_jump_table_addr(void)77738918e0bSBorislav Petkov (AMD) static u64 __init get_jump_table_addr(void)
77838918e0bSBorislav Petkov (AMD) {
77938918e0bSBorislav Petkov (AMD) struct ghcb_state state;
78038918e0bSBorislav Petkov (AMD) unsigned long flags;
78138918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
78238918e0bSBorislav Petkov (AMD) u64 ret = 0;
78338918e0bSBorislav Petkov (AMD)
78438918e0bSBorislav Petkov (AMD) if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
78538918e0bSBorislav Petkov (AMD) return get_snp_jump_table_addr();
78638918e0bSBorislav Petkov (AMD)
78738918e0bSBorislav Petkov (AMD) local_irq_save(flags);
78838918e0bSBorislav Petkov (AMD)
78938918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
79038918e0bSBorislav Petkov (AMD)
79138918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(ghcb);
79238918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_AP_JUMP_TABLE);
79338918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_1(ghcb, SVM_VMGEXIT_GET_AP_JUMP_TABLE);
79438918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_2(ghcb, 0);
79538918e0bSBorislav Petkov (AMD)
79638918e0bSBorislav Petkov (AMD) sev_es_wr_ghcb_msr(__pa(ghcb));
79738918e0bSBorislav Petkov (AMD) VMGEXIT();
79838918e0bSBorislav Petkov (AMD)
79938918e0bSBorislav Petkov (AMD) if (ghcb_sw_exit_info_1_is_valid(ghcb) &&
80038918e0bSBorislav Petkov (AMD) ghcb_sw_exit_info_2_is_valid(ghcb))
80138918e0bSBorislav Petkov (AMD) ret = ghcb->save.sw_exit_info_2;
80238918e0bSBorislav Petkov (AMD)
80338918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
80438918e0bSBorislav Petkov (AMD)
80538918e0bSBorislav Petkov (AMD) local_irq_restore(flags);
80638918e0bSBorislav Petkov (AMD)
80738918e0bSBorislav Petkov (AMD) return ret;
80838918e0bSBorislav Petkov (AMD) }
80938918e0bSBorislav Petkov (AMD)
81038918e0bSBorislav Petkov (AMD) static void __head
early_set_pages_state(unsigned long vaddr,unsigned long paddr,unsigned long npages,enum psc_op op)81138918e0bSBorislav Petkov (AMD) early_set_pages_state(unsigned long vaddr, unsigned long paddr,
81238918e0bSBorislav Petkov (AMD) unsigned long npages, enum psc_op op)
81338918e0bSBorislav Petkov (AMD) {
81438918e0bSBorislav Petkov (AMD) unsigned long paddr_end;
81538918e0bSBorislav Petkov (AMD) u64 val;
81638918e0bSBorislav Petkov (AMD)
81738918e0bSBorislav Petkov (AMD) vaddr = vaddr & PAGE_MASK;
81838918e0bSBorislav Petkov (AMD)
81938918e0bSBorislav Petkov (AMD) paddr = paddr & PAGE_MASK;
82038918e0bSBorislav Petkov (AMD) paddr_end = paddr + (npages << PAGE_SHIFT);
82138918e0bSBorislav Petkov (AMD)
82238918e0bSBorislav Petkov (AMD) while (paddr < paddr_end) {
82338918e0bSBorislav Petkov (AMD) /* Page validation must be rescinded before changing to shared */
82438918e0bSBorislav Petkov (AMD) if (op == SNP_PAGE_STATE_SHARED)
82538918e0bSBorislav Petkov (AMD) pvalidate_4k_page(vaddr, paddr, false);
82638918e0bSBorislav Petkov (AMD)
82738918e0bSBorislav Petkov (AMD) /*
82838918e0bSBorislav Petkov (AMD) * Use the MSR protocol because this function can be called before
82938918e0bSBorislav Petkov (AMD) * the GHCB is established.
83038918e0bSBorislav Petkov (AMD) */
83138918e0bSBorislav Petkov (AMD) sev_es_wr_ghcb_msr(GHCB_MSR_PSC_REQ_GFN(paddr >> PAGE_SHIFT, op));
83238918e0bSBorislav Petkov (AMD) VMGEXIT();
83338918e0bSBorislav Petkov (AMD)
83438918e0bSBorislav Petkov (AMD) val = sev_es_rd_ghcb_msr();
83538918e0bSBorislav Petkov (AMD)
83638918e0bSBorislav Petkov (AMD) if (WARN(GHCB_RESP_CODE(val) != GHCB_MSR_PSC_RESP,
83738918e0bSBorislav Petkov (AMD) "Wrong PSC response code: 0x%x\n",
83838918e0bSBorislav Petkov (AMD) (unsigned int)GHCB_RESP_CODE(val)))
83938918e0bSBorislav Petkov (AMD) goto e_term;
84038918e0bSBorislav Petkov (AMD)
84138918e0bSBorislav Petkov (AMD) if (WARN(GHCB_MSR_PSC_RESP_VAL(val),
84238918e0bSBorislav Petkov (AMD) "Failed to change page state to '%s' paddr 0x%lx error 0x%llx\n",
84338918e0bSBorislav Petkov (AMD) op == SNP_PAGE_STATE_PRIVATE ? "private" : "shared",
84438918e0bSBorislav Petkov (AMD) paddr, GHCB_MSR_PSC_RESP_VAL(val)))
84538918e0bSBorislav Petkov (AMD) goto e_term;
84638918e0bSBorislav Petkov (AMD)
84738918e0bSBorislav Petkov (AMD) /* Page validation must be performed after changing to private */
84838918e0bSBorislav Petkov (AMD) if (op == SNP_PAGE_STATE_PRIVATE)
84938918e0bSBorislav Petkov (AMD) pvalidate_4k_page(vaddr, paddr, true);
85038918e0bSBorislav Petkov (AMD)
85138918e0bSBorislav Petkov (AMD) vaddr += PAGE_SIZE;
85238918e0bSBorislav Petkov (AMD) paddr += PAGE_SIZE;
85338918e0bSBorislav Petkov (AMD) }
85438918e0bSBorislav Petkov (AMD)
85538918e0bSBorislav Petkov (AMD) return;
85638918e0bSBorislav Petkov (AMD)
85738918e0bSBorislav Petkov (AMD) e_term:
85838918e0bSBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
85938918e0bSBorislav Petkov (AMD) }
86038918e0bSBorislav Petkov (AMD)
early_snp_set_memory_private(unsigned long vaddr,unsigned long paddr,unsigned long npages)86138918e0bSBorislav Petkov (AMD) void __head early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
86238918e0bSBorislav Petkov (AMD) unsigned long npages)
86338918e0bSBorislav Petkov (AMD) {
86438918e0bSBorislav Petkov (AMD) /*
86538918e0bSBorislav Petkov (AMD) * This can be invoked in early boot while running identity mapped, so
86638918e0bSBorislav Petkov (AMD) * use an open coded check for SNP instead of using cc_platform_has().
86738918e0bSBorislav Petkov (AMD) * This eliminates worries about jump tables or checking boot_cpu_data
86838918e0bSBorislav Petkov (AMD) * in the cc_platform_has() function.
86938918e0bSBorislav Petkov (AMD) */
87038918e0bSBorislav Petkov (AMD) if (!(RIP_REL_REF(sev_status) & MSR_AMD64_SEV_SNP_ENABLED))
87138918e0bSBorislav Petkov (AMD) return;
87238918e0bSBorislav Petkov (AMD)
87338918e0bSBorislav Petkov (AMD) /*
87438918e0bSBorislav Petkov (AMD) * Ask the hypervisor to mark the memory pages as private in the RMP
87538918e0bSBorislav Petkov (AMD) * table.
87638918e0bSBorislav Petkov (AMD) */
87738918e0bSBorislav Petkov (AMD) early_set_pages_state(vaddr, paddr, npages, SNP_PAGE_STATE_PRIVATE);
87838918e0bSBorislav Petkov (AMD) }
87938918e0bSBorislav Petkov (AMD)
early_snp_set_memory_shared(unsigned long vaddr,unsigned long paddr,unsigned long npages)88038918e0bSBorislav Petkov (AMD) void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr,
88138918e0bSBorislav Petkov (AMD) unsigned long npages)
88238918e0bSBorislav Petkov (AMD) {
88338918e0bSBorislav Petkov (AMD) /*
88438918e0bSBorislav Petkov (AMD) * This can be invoked in early boot while running identity mapped, so
88538918e0bSBorislav Petkov (AMD) * use an open coded check for SNP instead of using cc_platform_has().
88638918e0bSBorislav Petkov (AMD) * This eliminates worries about jump tables or checking boot_cpu_data
88738918e0bSBorislav Petkov (AMD) * in the cc_platform_has() function.
88838918e0bSBorislav Petkov (AMD) */
88938918e0bSBorislav Petkov (AMD) if (!(RIP_REL_REF(sev_status) & MSR_AMD64_SEV_SNP_ENABLED))
89038918e0bSBorislav Petkov (AMD) return;
89138918e0bSBorislav Petkov (AMD)
89238918e0bSBorislav Petkov (AMD) /* Ask hypervisor to mark the memory pages shared in the RMP table. */
89338918e0bSBorislav Petkov (AMD) early_set_pages_state(vaddr, paddr, npages, SNP_PAGE_STATE_SHARED);
89438918e0bSBorislav Petkov (AMD) }
89538918e0bSBorislav Petkov (AMD)
__set_pages_state(struct snp_psc_desc * data,unsigned long vaddr,unsigned long vaddr_end,int op)89638918e0bSBorislav Petkov (AMD) static unsigned long __set_pages_state(struct snp_psc_desc *data, unsigned long vaddr,
89738918e0bSBorislav Petkov (AMD) unsigned long vaddr_end, int op)
89838918e0bSBorislav Petkov (AMD) {
89938918e0bSBorislav Petkov (AMD) struct ghcb_state state;
90038918e0bSBorislav Petkov (AMD) bool use_large_entry;
90138918e0bSBorislav Petkov (AMD) struct psc_hdr *hdr;
90238918e0bSBorislav Petkov (AMD) struct psc_entry *e;
90338918e0bSBorislav Petkov (AMD) unsigned long flags;
90438918e0bSBorislav Petkov (AMD) unsigned long pfn;
90538918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
90638918e0bSBorislav Petkov (AMD) int i;
90738918e0bSBorislav Petkov (AMD)
90838918e0bSBorislav Petkov (AMD) hdr = &data->hdr;
90938918e0bSBorislav Petkov (AMD) e = data->entries;
91038918e0bSBorislav Petkov (AMD)
91138918e0bSBorislav Petkov (AMD) memset(data, 0, sizeof(*data));
91238918e0bSBorislav Petkov (AMD) i = 0;
91338918e0bSBorislav Petkov (AMD)
91438918e0bSBorislav Petkov (AMD) while (vaddr < vaddr_end && i < ARRAY_SIZE(data->entries)) {
91538918e0bSBorislav Petkov (AMD) hdr->end_entry = i;
91638918e0bSBorislav Petkov (AMD)
91738918e0bSBorislav Petkov (AMD) if (is_vmalloc_addr((void *)vaddr)) {
91838918e0bSBorislav Petkov (AMD) pfn = vmalloc_to_pfn((void *)vaddr);
91938918e0bSBorislav Petkov (AMD) use_large_entry = false;
92038918e0bSBorislav Petkov (AMD) } else {
92138918e0bSBorislav Petkov (AMD) pfn = __pa(vaddr) >> PAGE_SHIFT;
92238918e0bSBorislav Petkov (AMD) use_large_entry = true;
92338918e0bSBorislav Petkov (AMD) }
92438918e0bSBorislav Petkov (AMD)
92538918e0bSBorislav Petkov (AMD) e->gfn = pfn;
92638918e0bSBorislav Petkov (AMD) e->operation = op;
92738918e0bSBorislav Petkov (AMD)
92838918e0bSBorislav Petkov (AMD) if (use_large_entry && IS_ALIGNED(vaddr, PMD_SIZE) &&
92938918e0bSBorislav Petkov (AMD) (vaddr_end - vaddr) >= PMD_SIZE) {
93038918e0bSBorislav Petkov (AMD) e->pagesize = RMP_PG_SIZE_2M;
93138918e0bSBorislav Petkov (AMD) vaddr += PMD_SIZE;
93238918e0bSBorislav Petkov (AMD) } else {
93338918e0bSBorislav Petkov (AMD) e->pagesize = RMP_PG_SIZE_4K;
93438918e0bSBorislav Petkov (AMD) vaddr += PAGE_SIZE;
93538918e0bSBorislav Petkov (AMD) }
93638918e0bSBorislav Petkov (AMD)
93738918e0bSBorislav Petkov (AMD) e++;
93838918e0bSBorislav Petkov (AMD) i++;
93938918e0bSBorislav Petkov (AMD) }
94038918e0bSBorislav Petkov (AMD)
94138918e0bSBorislav Petkov (AMD) /* Page validation must be rescinded before changing to shared */
94238918e0bSBorislav Petkov (AMD) if (op == SNP_PAGE_STATE_SHARED)
94338918e0bSBorislav Petkov (AMD) pvalidate_pages(data);
94438918e0bSBorislav Petkov (AMD)
94538918e0bSBorislav Petkov (AMD) local_irq_save(flags);
94638918e0bSBorislav Petkov (AMD)
94738918e0bSBorislav Petkov (AMD) if (sev_cfg.ghcbs_initialized)
94838918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
94938918e0bSBorislav Petkov (AMD) else
95038918e0bSBorislav Petkov (AMD) ghcb = boot_ghcb;
95138918e0bSBorislav Petkov (AMD)
95238918e0bSBorislav Petkov (AMD) /* Invoke the hypervisor to perform the page state changes */
95338918e0bSBorislav Petkov (AMD) if (!ghcb || vmgexit_psc(ghcb, data))
95438918e0bSBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
95538918e0bSBorislav Petkov (AMD)
95638918e0bSBorislav Petkov (AMD) if (sev_cfg.ghcbs_initialized)
95738918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
95838918e0bSBorislav Petkov (AMD)
95938918e0bSBorislav Petkov (AMD) local_irq_restore(flags);
96038918e0bSBorislav Petkov (AMD)
96138918e0bSBorislav Petkov (AMD) /* Page validation must be performed after changing to private */
96238918e0bSBorislav Petkov (AMD) if (op == SNP_PAGE_STATE_PRIVATE)
96338918e0bSBorislav Petkov (AMD) pvalidate_pages(data);
96438918e0bSBorislav Petkov (AMD)
96538918e0bSBorislav Petkov (AMD) return vaddr;
96638918e0bSBorislav Petkov (AMD) }
96738918e0bSBorislav Petkov (AMD)
set_pages_state(unsigned long vaddr,unsigned long npages,int op)96838918e0bSBorislav Petkov (AMD) static void set_pages_state(unsigned long vaddr, unsigned long npages, int op)
96938918e0bSBorislav Petkov (AMD) {
97038918e0bSBorislav Petkov (AMD) struct snp_psc_desc desc;
97138918e0bSBorislav Petkov (AMD) unsigned long vaddr_end;
97238918e0bSBorislav Petkov (AMD)
97338918e0bSBorislav Petkov (AMD) /* Use the MSR protocol when a GHCB is not available. */
97438918e0bSBorislav Petkov (AMD) if (!boot_ghcb)
97538918e0bSBorislav Petkov (AMD) return early_set_pages_state(vaddr, __pa(vaddr), npages, op);
97638918e0bSBorislav Petkov (AMD)
97738918e0bSBorislav Petkov (AMD) vaddr = vaddr & PAGE_MASK;
97838918e0bSBorislav Petkov (AMD) vaddr_end = vaddr + (npages << PAGE_SHIFT);
97938918e0bSBorislav Petkov (AMD)
98038918e0bSBorislav Petkov (AMD) while (vaddr < vaddr_end)
98138918e0bSBorislav Petkov (AMD) vaddr = __set_pages_state(&desc, vaddr, vaddr_end, op);
98238918e0bSBorislav Petkov (AMD) }
98338918e0bSBorislav Petkov (AMD)
snp_set_memory_shared(unsigned long vaddr,unsigned long npages)98438918e0bSBorislav Petkov (AMD) void snp_set_memory_shared(unsigned long vaddr, unsigned long npages)
98538918e0bSBorislav Petkov (AMD) {
98638918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
98738918e0bSBorislav Petkov (AMD) return;
98838918e0bSBorislav Petkov (AMD)
98938918e0bSBorislav Petkov (AMD) set_pages_state(vaddr, npages, SNP_PAGE_STATE_SHARED);
99038918e0bSBorislav Petkov (AMD) }
99138918e0bSBorislav Petkov (AMD)
snp_set_memory_private(unsigned long vaddr,unsigned long npages)99238918e0bSBorislav Petkov (AMD) void snp_set_memory_private(unsigned long vaddr, unsigned long npages)
99338918e0bSBorislav Petkov (AMD) {
99438918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
99538918e0bSBorislav Petkov (AMD) return;
99638918e0bSBorislav Petkov (AMD)
99738918e0bSBorislav Petkov (AMD) set_pages_state(vaddr, npages, SNP_PAGE_STATE_PRIVATE);
99838918e0bSBorislav Petkov (AMD) }
99938918e0bSBorislav Petkov (AMD)
snp_accept_memory(phys_addr_t start,phys_addr_t end)100038918e0bSBorislav Petkov (AMD) void snp_accept_memory(phys_addr_t start, phys_addr_t end)
100138918e0bSBorislav Petkov (AMD) {
100238918e0bSBorislav Petkov (AMD) unsigned long vaddr, npages;
100338918e0bSBorislav Petkov (AMD)
100438918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
100538918e0bSBorislav Petkov (AMD) return;
100638918e0bSBorislav Petkov (AMD)
100738918e0bSBorislav Petkov (AMD) vaddr = (unsigned long)__va(start);
100838918e0bSBorislav Petkov (AMD) npages = (end - start) >> PAGE_SHIFT;
100938918e0bSBorislav Petkov (AMD)
101038918e0bSBorislav Petkov (AMD) set_pages_state(vaddr, npages, SNP_PAGE_STATE_PRIVATE);
101138918e0bSBorislav Petkov (AMD) }
101238918e0bSBorislav Petkov (AMD)
snp_set_vmsa(void * va,void * caa,int apic_id,bool make_vmsa)101338918e0bSBorislav Petkov (AMD) static int snp_set_vmsa(void *va, void *caa, int apic_id, bool make_vmsa)
101438918e0bSBorislav Petkov (AMD) {
101538918e0bSBorislav Petkov (AMD) int ret;
101638918e0bSBorislav Petkov (AMD)
101738918e0bSBorislav Petkov (AMD) if (snp_vmpl) {
101838918e0bSBorislav Petkov (AMD) struct svsm_call call = {};
101938918e0bSBorislav Petkov (AMD) unsigned long flags;
102038918e0bSBorislav Petkov (AMD)
102138918e0bSBorislav Petkov (AMD) local_irq_save(flags);
102238918e0bSBorislav Petkov (AMD)
102338918e0bSBorislav Petkov (AMD) call.caa = this_cpu_read(svsm_caa);
102438918e0bSBorislav Petkov (AMD) call.rcx = __pa(va);
102538918e0bSBorislav Petkov (AMD)
102638918e0bSBorislav Petkov (AMD) if (make_vmsa) {
102738918e0bSBorislav Petkov (AMD) /* Protocol 0, Call ID 2 */
102838918e0bSBorislav Petkov (AMD) call.rax = SVSM_CORE_CALL(SVSM_CORE_CREATE_VCPU);
102938918e0bSBorislav Petkov (AMD) call.rdx = __pa(caa);
103038918e0bSBorislav Petkov (AMD) call.r8 = apic_id;
103138918e0bSBorislav Petkov (AMD) } else {
103238918e0bSBorislav Petkov (AMD) /* Protocol 0, Call ID 3 */
103338918e0bSBorislav Petkov (AMD) call.rax = SVSM_CORE_CALL(SVSM_CORE_DELETE_VCPU);
103438918e0bSBorislav Petkov (AMD) }
103538918e0bSBorislav Petkov (AMD)
103638918e0bSBorislav Petkov (AMD) ret = svsm_perform_call_protocol(&call);
103738918e0bSBorislav Petkov (AMD)
103838918e0bSBorislav Petkov (AMD) local_irq_restore(flags);
103938918e0bSBorislav Petkov (AMD) } else {
104038918e0bSBorislav Petkov (AMD) /*
104138918e0bSBorislav Petkov (AMD) * If the kernel runs at VMPL0, it can change the VMSA
104238918e0bSBorislav Petkov (AMD) * bit for a page using the RMPADJUST instruction.
104338918e0bSBorislav Petkov (AMD) * However, for the instruction to succeed it must
104438918e0bSBorislav Petkov (AMD) * target the permissions of a lesser privileged (higher
104538918e0bSBorislav Petkov (AMD) * numbered) VMPL level, so use VMPL1.
104638918e0bSBorislav Petkov (AMD) */
104738918e0bSBorislav Petkov (AMD) u64 attrs = 1;
104838918e0bSBorislav Petkov (AMD)
104938918e0bSBorislav Petkov (AMD) if (make_vmsa)
105038918e0bSBorislav Petkov (AMD) attrs |= RMPADJUST_VMSA_PAGE_BIT;
105138918e0bSBorislav Petkov (AMD)
105238918e0bSBorislav Petkov (AMD) ret = rmpadjust((unsigned long)va, RMP_PG_SIZE_4K, attrs);
105338918e0bSBorislav Petkov (AMD) }
105438918e0bSBorislav Petkov (AMD)
105538918e0bSBorislav Petkov (AMD) return ret;
105638918e0bSBorislav Petkov (AMD) }
105738918e0bSBorislav Petkov (AMD)
105838918e0bSBorislav Petkov (AMD) #define __ATTR_BASE (SVM_SELECTOR_P_MASK | SVM_SELECTOR_S_MASK)
105938918e0bSBorislav Petkov (AMD) #define INIT_CS_ATTRIBS (__ATTR_BASE | SVM_SELECTOR_READ_MASK | SVM_SELECTOR_CODE_MASK)
106038918e0bSBorislav Petkov (AMD) #define INIT_DS_ATTRIBS (__ATTR_BASE | SVM_SELECTOR_WRITE_MASK)
106138918e0bSBorislav Petkov (AMD)
106238918e0bSBorislav Petkov (AMD) #define INIT_LDTR_ATTRIBS (SVM_SELECTOR_P_MASK | 2)
106338918e0bSBorislav Petkov (AMD) #define INIT_TR_ATTRIBS (SVM_SELECTOR_P_MASK | 3)
106438918e0bSBorislav Petkov (AMD)
snp_alloc_vmsa_page(int cpu)106538918e0bSBorislav Petkov (AMD) static void *snp_alloc_vmsa_page(int cpu)
106638918e0bSBorislav Petkov (AMD) {
106738918e0bSBorislav Petkov (AMD) struct page *p;
106838918e0bSBorislav Petkov (AMD)
106938918e0bSBorislav Petkov (AMD) /*
107038918e0bSBorislav Petkov (AMD) * Allocate VMSA page to work around the SNP erratum where the CPU will
107138918e0bSBorislav Petkov (AMD) * incorrectly signal an RMP violation #PF if a large page (2MB or 1GB)
107238918e0bSBorislav Petkov (AMD) * collides with the RMP entry of VMSA page. The recommended workaround
107338918e0bSBorislav Petkov (AMD) * is to not use a large page.
107438918e0bSBorislav Petkov (AMD) *
107538918e0bSBorislav Petkov (AMD) * Allocate an 8k page which is also 8k-aligned.
107638918e0bSBorislav Petkov (AMD) */
107738918e0bSBorislav Petkov (AMD) p = alloc_pages_node(cpu_to_node(cpu), GFP_KERNEL_ACCOUNT | __GFP_ZERO, 1);
107838918e0bSBorislav Petkov (AMD) if (!p)
107938918e0bSBorislav Petkov (AMD) return NULL;
108038918e0bSBorislav Petkov (AMD)
108138918e0bSBorislav Petkov (AMD) split_page(p, 1);
108238918e0bSBorislav Petkov (AMD)
108338918e0bSBorislav Petkov (AMD) /* Free the first 4k. This page may be 2M/1G aligned and cannot be used. */
108438918e0bSBorislav Petkov (AMD) __free_page(p);
108538918e0bSBorislav Petkov (AMD)
108638918e0bSBorislav Petkov (AMD) return page_address(p + 1);
108738918e0bSBorislav Petkov (AMD) }
108838918e0bSBorislav Petkov (AMD)
snp_cleanup_vmsa(struct sev_es_save_area * vmsa,int apic_id)108938918e0bSBorislav Petkov (AMD) static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa, int apic_id)
109038918e0bSBorislav Petkov (AMD) {
109138918e0bSBorislav Petkov (AMD) int err;
109238918e0bSBorislav Petkov (AMD)
109338918e0bSBorislav Petkov (AMD) err = snp_set_vmsa(vmsa, NULL, apic_id, false);
109438918e0bSBorislav Petkov (AMD) if (err)
109538918e0bSBorislav Petkov (AMD) pr_err("clear VMSA page failed (%u), leaking page\n", err);
109638918e0bSBorislav Petkov (AMD) else
109738918e0bSBorislav Petkov (AMD) free_page((unsigned long)vmsa);
109838918e0bSBorislav Petkov (AMD) }
109938918e0bSBorislav Petkov (AMD)
wakeup_cpu_via_vmgexit(u32 apic_id,unsigned long start_ip)110038918e0bSBorislav Petkov (AMD) static int wakeup_cpu_via_vmgexit(u32 apic_id, unsigned long start_ip)
110138918e0bSBorislav Petkov (AMD) {
110238918e0bSBorislav Petkov (AMD) struct sev_es_save_area *cur_vmsa, *vmsa;
110338918e0bSBorislav Petkov (AMD) struct ghcb_state state;
110438918e0bSBorislav Petkov (AMD) struct svsm_ca *caa;
110538918e0bSBorislav Petkov (AMD) unsigned long flags;
110638918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
110738918e0bSBorislav Petkov (AMD) u8 sipi_vector;
110838918e0bSBorislav Petkov (AMD) int cpu, ret;
110938918e0bSBorislav Petkov (AMD) u64 cr4;
111038918e0bSBorislav Petkov (AMD)
111138918e0bSBorislav Petkov (AMD) /*
111238918e0bSBorislav Petkov (AMD) * The hypervisor SNP feature support check has happened earlier, just check
111338918e0bSBorislav Petkov (AMD) * the AP_CREATION one here.
111438918e0bSBorislav Petkov (AMD) */
111538918e0bSBorislav Petkov (AMD) if (!(sev_hv_features & GHCB_HV_FT_SNP_AP_CREATION))
111638918e0bSBorislav Petkov (AMD) return -EOPNOTSUPP;
111738918e0bSBorislav Petkov (AMD)
111838918e0bSBorislav Petkov (AMD) /*
111938918e0bSBorislav Petkov (AMD) * Verify the desired start IP against the known trampoline start IP
112038918e0bSBorislav Petkov (AMD) * to catch any future new trampolines that may be introduced that
112138918e0bSBorislav Petkov (AMD) * would require a new protected guest entry point.
112238918e0bSBorislav Petkov (AMD) */
112338918e0bSBorislav Petkov (AMD) if (WARN_ONCE(start_ip != real_mode_header->trampoline_start,
112438918e0bSBorislav Petkov (AMD) "Unsupported SNP start_ip: %lx\n", start_ip))
112538918e0bSBorislav Petkov (AMD) return -EINVAL;
112638918e0bSBorislav Petkov (AMD)
112738918e0bSBorislav Petkov (AMD) /* Override start_ip with known protected guest start IP */
112838918e0bSBorislav Petkov (AMD) start_ip = real_mode_header->sev_es_trampoline_start;
112938918e0bSBorislav Petkov (AMD)
113038918e0bSBorislav Petkov (AMD) /* Find the logical CPU for the APIC ID */
113138918e0bSBorislav Petkov (AMD) for_each_present_cpu(cpu) {
113238918e0bSBorislav Petkov (AMD) if (arch_match_cpu_phys_id(cpu, apic_id))
113338918e0bSBorislav Petkov (AMD) break;
113438918e0bSBorislav Petkov (AMD) }
113538918e0bSBorislav Petkov (AMD) if (cpu >= nr_cpu_ids)
113638918e0bSBorislav Petkov (AMD) return -EINVAL;
113738918e0bSBorislav Petkov (AMD)
113838918e0bSBorislav Petkov (AMD) cur_vmsa = per_cpu(sev_vmsa, cpu);
113938918e0bSBorislav Petkov (AMD)
114038918e0bSBorislav Petkov (AMD) /*
114138918e0bSBorislav Petkov (AMD) * A new VMSA is created each time because there is no guarantee that
114238918e0bSBorislav Petkov (AMD) * the current VMSA is the kernels or that the vCPU is not running. If
114338918e0bSBorislav Petkov (AMD) * an attempt was done to use the current VMSA with a running vCPU, a
114438918e0bSBorislav Petkov (AMD) * #VMEXIT of that vCPU would wipe out all of the settings being done
114538918e0bSBorislav Petkov (AMD) * here.
114638918e0bSBorislav Petkov (AMD) */
114738918e0bSBorislav Petkov (AMD) vmsa = (struct sev_es_save_area *)snp_alloc_vmsa_page(cpu);
114838918e0bSBorislav Petkov (AMD) if (!vmsa)
114938918e0bSBorislav Petkov (AMD) return -ENOMEM;
115038918e0bSBorislav Petkov (AMD)
115138918e0bSBorislav Petkov (AMD) /* If an SVSM is present, the SVSM per-CPU CAA will be !NULL */
115238918e0bSBorislav Petkov (AMD) caa = per_cpu(svsm_caa, cpu);
115338918e0bSBorislav Petkov (AMD)
115438918e0bSBorislav Petkov (AMD) /* CR4 should maintain the MCE value */
115538918e0bSBorislav Petkov (AMD) cr4 = native_read_cr4() & X86_CR4_MCE;
115638918e0bSBorislav Petkov (AMD)
115738918e0bSBorislav Petkov (AMD) /* Set the CS value based on the start_ip converted to a SIPI vector */
115838918e0bSBorislav Petkov (AMD) sipi_vector = (start_ip >> 12);
115938918e0bSBorislav Petkov (AMD) vmsa->cs.base = sipi_vector << 12;
116038918e0bSBorislav Petkov (AMD) vmsa->cs.limit = AP_INIT_CS_LIMIT;
116138918e0bSBorislav Petkov (AMD) vmsa->cs.attrib = INIT_CS_ATTRIBS;
116238918e0bSBorislav Petkov (AMD) vmsa->cs.selector = sipi_vector << 8;
116338918e0bSBorislav Petkov (AMD)
116438918e0bSBorislav Petkov (AMD) /* Set the RIP value based on start_ip */
116538918e0bSBorislav Petkov (AMD) vmsa->rip = start_ip & 0xfff;
116638918e0bSBorislav Petkov (AMD)
116738918e0bSBorislav Petkov (AMD) /* Set AP INIT defaults as documented in the APM */
116838918e0bSBorislav Petkov (AMD) vmsa->ds.limit = AP_INIT_DS_LIMIT;
116938918e0bSBorislav Petkov (AMD) vmsa->ds.attrib = INIT_DS_ATTRIBS;
117038918e0bSBorislav Petkov (AMD) vmsa->es = vmsa->ds;
117138918e0bSBorislav Petkov (AMD) vmsa->fs = vmsa->ds;
117238918e0bSBorislav Petkov (AMD) vmsa->gs = vmsa->ds;
117338918e0bSBorislav Petkov (AMD) vmsa->ss = vmsa->ds;
117438918e0bSBorislav Petkov (AMD)
117538918e0bSBorislav Petkov (AMD) vmsa->gdtr.limit = AP_INIT_GDTR_LIMIT;
117638918e0bSBorislav Petkov (AMD) vmsa->ldtr.limit = AP_INIT_LDTR_LIMIT;
117738918e0bSBorislav Petkov (AMD) vmsa->ldtr.attrib = INIT_LDTR_ATTRIBS;
117838918e0bSBorislav Petkov (AMD) vmsa->idtr.limit = AP_INIT_IDTR_LIMIT;
117938918e0bSBorislav Petkov (AMD) vmsa->tr.limit = AP_INIT_TR_LIMIT;
118038918e0bSBorislav Petkov (AMD) vmsa->tr.attrib = INIT_TR_ATTRIBS;
118138918e0bSBorislav Petkov (AMD)
118238918e0bSBorislav Petkov (AMD) vmsa->cr4 = cr4;
118338918e0bSBorislav Petkov (AMD) vmsa->cr0 = AP_INIT_CR0_DEFAULT;
118438918e0bSBorislav Petkov (AMD) vmsa->dr7 = DR7_RESET_VALUE;
118538918e0bSBorislav Petkov (AMD) vmsa->dr6 = AP_INIT_DR6_DEFAULT;
118638918e0bSBorislav Petkov (AMD) vmsa->rflags = AP_INIT_RFLAGS_DEFAULT;
118738918e0bSBorislav Petkov (AMD) vmsa->g_pat = AP_INIT_GPAT_DEFAULT;
118838918e0bSBorislav Petkov (AMD) vmsa->xcr0 = AP_INIT_XCR0_DEFAULT;
118938918e0bSBorislav Petkov (AMD) vmsa->mxcsr = AP_INIT_MXCSR_DEFAULT;
119038918e0bSBorislav Petkov (AMD) vmsa->x87_ftw = AP_INIT_X87_FTW_DEFAULT;
119138918e0bSBorislav Petkov (AMD) vmsa->x87_fcw = AP_INIT_X87_FCW_DEFAULT;
119238918e0bSBorislav Petkov (AMD)
119338918e0bSBorislav Petkov (AMD) /* SVME must be set. */
119438918e0bSBorislav Petkov (AMD) vmsa->efer = EFER_SVME;
119538918e0bSBorislav Petkov (AMD)
119638918e0bSBorislav Petkov (AMD) /*
119738918e0bSBorislav Petkov (AMD) * Set the SNP-specific fields for this VMSA:
119838918e0bSBorislav Petkov (AMD) * VMPL level
119938918e0bSBorislav Petkov (AMD) * SEV_FEATURES (matches the SEV STATUS MSR right shifted 2 bits)
120038918e0bSBorislav Petkov (AMD) */
120138918e0bSBorislav Petkov (AMD) vmsa->vmpl = snp_vmpl;
120238918e0bSBorislav Petkov (AMD) vmsa->sev_features = sev_status >> 2;
120338918e0bSBorislav Petkov (AMD)
120438918e0bSBorislav Petkov (AMD) /* Switch the page over to a VMSA page now that it is initialized */
120538918e0bSBorislav Petkov (AMD) ret = snp_set_vmsa(vmsa, caa, apic_id, true);
120638918e0bSBorislav Petkov (AMD) if (ret) {
120738918e0bSBorislav Petkov (AMD) pr_err("set VMSA page failed (%u)\n", ret);
120838918e0bSBorislav Petkov (AMD) free_page((unsigned long)vmsa);
120938918e0bSBorislav Petkov (AMD)
121038918e0bSBorislav Petkov (AMD) return -EINVAL;
121138918e0bSBorislav Petkov (AMD) }
121238918e0bSBorislav Petkov (AMD)
121338918e0bSBorislav Petkov (AMD) /* Issue VMGEXIT AP Creation NAE event */
121438918e0bSBorislav Petkov (AMD) local_irq_save(flags);
121538918e0bSBorislav Petkov (AMD)
121638918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
121738918e0bSBorislav Petkov (AMD)
121838918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(ghcb);
121938918e0bSBorislav Petkov (AMD) ghcb_set_rax(ghcb, vmsa->sev_features);
122038918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_AP_CREATION);
122138918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_1(ghcb,
122238918e0bSBorislav Petkov (AMD) ((u64)apic_id << 32) |
122338918e0bSBorislav Petkov (AMD) ((u64)snp_vmpl << 16) |
122438918e0bSBorislav Petkov (AMD) SVM_VMGEXIT_AP_CREATE);
122538918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_2(ghcb, __pa(vmsa));
122638918e0bSBorislav Petkov (AMD)
122738918e0bSBorislav Petkov (AMD) sev_es_wr_ghcb_msr(__pa(ghcb));
122838918e0bSBorislav Petkov (AMD) VMGEXIT();
122938918e0bSBorislav Petkov (AMD)
123038918e0bSBorislav Petkov (AMD) if (!ghcb_sw_exit_info_1_is_valid(ghcb) ||
123138918e0bSBorislav Petkov (AMD) lower_32_bits(ghcb->save.sw_exit_info_1)) {
123238918e0bSBorislav Petkov (AMD) pr_err("SNP AP Creation error\n");
123338918e0bSBorislav Petkov (AMD) ret = -EINVAL;
123438918e0bSBorislav Petkov (AMD) }
123538918e0bSBorislav Petkov (AMD)
123638918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
123738918e0bSBorislav Petkov (AMD)
123838918e0bSBorislav Petkov (AMD) local_irq_restore(flags);
123938918e0bSBorislav Petkov (AMD)
124038918e0bSBorislav Petkov (AMD) /* Perform cleanup if there was an error */
124138918e0bSBorislav Petkov (AMD) if (ret) {
124238918e0bSBorislav Petkov (AMD) snp_cleanup_vmsa(vmsa, apic_id);
124338918e0bSBorislav Petkov (AMD) vmsa = NULL;
124438918e0bSBorislav Petkov (AMD) }
124538918e0bSBorislav Petkov (AMD)
124638918e0bSBorislav Petkov (AMD) /* Free up any previous VMSA page */
124738918e0bSBorislav Petkov (AMD) if (cur_vmsa)
124838918e0bSBorislav Petkov (AMD) snp_cleanup_vmsa(cur_vmsa, apic_id);
124938918e0bSBorislav Petkov (AMD)
125038918e0bSBorislav Petkov (AMD) /* Record the current VMSA page */
125138918e0bSBorislav Petkov (AMD) per_cpu(sev_vmsa, cpu) = vmsa;
125238918e0bSBorislav Petkov (AMD)
125338918e0bSBorislav Petkov (AMD) return ret;
125438918e0bSBorislav Petkov (AMD) }
125538918e0bSBorislav Petkov (AMD)
snp_set_wakeup_secondary_cpu(void)125638918e0bSBorislav Petkov (AMD) void __init snp_set_wakeup_secondary_cpu(void)
125738918e0bSBorislav Petkov (AMD) {
125838918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
125938918e0bSBorislav Petkov (AMD) return;
126038918e0bSBorislav Petkov (AMD)
126138918e0bSBorislav Petkov (AMD) /*
126238918e0bSBorislav Petkov (AMD) * Always set this override if SNP is enabled. This makes it the
126338918e0bSBorislav Petkov (AMD) * required method to start APs under SNP. If the hypervisor does
126438918e0bSBorislav Petkov (AMD) * not support AP creation, then no APs will be started.
126538918e0bSBorislav Petkov (AMD) */
126638918e0bSBorislav Petkov (AMD) apic_update_callback(wakeup_secondary_cpu, wakeup_cpu_via_vmgexit);
126738918e0bSBorislav Petkov (AMD) }
126838918e0bSBorislav Petkov (AMD)
sev_es_setup_ap_jump_table(struct real_mode_header * rmh)126938918e0bSBorislav Petkov (AMD) int __init sev_es_setup_ap_jump_table(struct real_mode_header *rmh)
127038918e0bSBorislav Petkov (AMD) {
127138918e0bSBorislav Petkov (AMD) u16 startup_cs, startup_ip;
127238918e0bSBorislav Petkov (AMD) phys_addr_t jump_table_pa;
127338918e0bSBorislav Petkov (AMD) u64 jump_table_addr;
127438918e0bSBorislav Petkov (AMD) u16 __iomem *jump_table;
127538918e0bSBorislav Petkov (AMD)
127638918e0bSBorislav Petkov (AMD) jump_table_addr = get_jump_table_addr();
127738918e0bSBorislav Petkov (AMD)
127838918e0bSBorislav Petkov (AMD) /* On UP guests there is no jump table so this is not a failure */
127938918e0bSBorislav Petkov (AMD) if (!jump_table_addr)
128038918e0bSBorislav Petkov (AMD) return 0;
128138918e0bSBorislav Petkov (AMD)
128238918e0bSBorislav Petkov (AMD) /* Check if AP Jump Table is page-aligned */
128338918e0bSBorislav Petkov (AMD) if (jump_table_addr & ~PAGE_MASK)
128438918e0bSBorislav Petkov (AMD) return -EINVAL;
128538918e0bSBorislav Petkov (AMD)
128638918e0bSBorislav Petkov (AMD) jump_table_pa = jump_table_addr & PAGE_MASK;
128738918e0bSBorislav Petkov (AMD)
128838918e0bSBorislav Petkov (AMD) startup_cs = (u16)(rmh->trampoline_start >> 4);
128938918e0bSBorislav Petkov (AMD) startup_ip = (u16)(rmh->sev_es_trampoline_start -
129038918e0bSBorislav Petkov (AMD) rmh->trampoline_start);
129138918e0bSBorislav Petkov (AMD)
129238918e0bSBorislav Petkov (AMD) jump_table = ioremap_encrypted(jump_table_pa, PAGE_SIZE);
129338918e0bSBorislav Petkov (AMD) if (!jump_table)
129438918e0bSBorislav Petkov (AMD) return -EIO;
129538918e0bSBorislav Petkov (AMD)
129638918e0bSBorislav Petkov (AMD) writew(startup_ip, &jump_table[0]);
129738918e0bSBorislav Petkov (AMD) writew(startup_cs, &jump_table[1]);
129838918e0bSBorislav Petkov (AMD)
129938918e0bSBorislav Petkov (AMD) iounmap(jump_table);
130038918e0bSBorislav Petkov (AMD)
130138918e0bSBorislav Petkov (AMD) return 0;
130238918e0bSBorislav Petkov (AMD) }
130338918e0bSBorislav Petkov (AMD)
130438918e0bSBorislav Petkov (AMD) /*
130538918e0bSBorislav Petkov (AMD) * This is needed by the OVMF UEFI firmware which will use whatever it finds in
130638918e0bSBorislav Petkov (AMD) * the GHCB MSR as its GHCB to talk to the hypervisor. So make sure the per-cpu
130738918e0bSBorislav Petkov (AMD) * runtime GHCBs used by the kernel are also mapped in the EFI page-table.
130838918e0bSBorislav Petkov (AMD) */
sev_es_efi_map_ghcbs(pgd_t * pgd)130938918e0bSBorislav Petkov (AMD) int __init sev_es_efi_map_ghcbs(pgd_t *pgd)
131038918e0bSBorislav Petkov (AMD) {
131138918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data;
131238918e0bSBorislav Petkov (AMD) unsigned long address, pflags;
131338918e0bSBorislav Petkov (AMD) int cpu;
131438918e0bSBorislav Petkov (AMD) u64 pfn;
131538918e0bSBorislav Petkov (AMD)
131638918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
131738918e0bSBorislav Petkov (AMD) return 0;
131838918e0bSBorislav Petkov (AMD)
131938918e0bSBorislav Petkov (AMD) pflags = _PAGE_NX | _PAGE_RW;
132038918e0bSBorislav Petkov (AMD)
132138918e0bSBorislav Petkov (AMD) for_each_possible_cpu(cpu) {
132238918e0bSBorislav Petkov (AMD) data = per_cpu(runtime_data, cpu);
132338918e0bSBorislav Petkov (AMD)
132438918e0bSBorislav Petkov (AMD) address = __pa(&data->ghcb_page);
132538918e0bSBorislav Petkov (AMD) pfn = address >> PAGE_SHIFT;
132638918e0bSBorislav Petkov (AMD)
132738918e0bSBorislav Petkov (AMD) if (kernel_map_pages_in_pgd(pgd, pfn, address, 1, pflags))
132838918e0bSBorislav Petkov (AMD) return 1;
132938918e0bSBorislav Petkov (AMD) }
133038918e0bSBorislav Petkov (AMD)
133138918e0bSBorislav Petkov (AMD) return 0;
133238918e0bSBorislav Petkov (AMD) }
133338918e0bSBorislav Petkov (AMD)
vc_handle_msr(struct ghcb * ghcb,struct es_em_ctxt * ctxt)133438918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
133538918e0bSBorislav Petkov (AMD) {
133638918e0bSBorislav Petkov (AMD) struct pt_regs *regs = ctxt->regs;
133738918e0bSBorislav Petkov (AMD) enum es_result ret;
133838918e0bSBorislav Petkov (AMD) u64 exit_info_1;
133938918e0bSBorislav Petkov (AMD)
134038918e0bSBorislav Petkov (AMD) /* Is it a WRMSR? */
134138918e0bSBorislav Petkov (AMD) exit_info_1 = (ctxt->insn.opcode.bytes[1] == 0x30) ? 1 : 0;
134238918e0bSBorislav Petkov (AMD)
134338918e0bSBorislav Petkov (AMD) if (regs->cx == MSR_SVSM_CAA) {
134438918e0bSBorislav Petkov (AMD) /* Writes to the SVSM CAA msr are ignored */
134538918e0bSBorislav Petkov (AMD) if (exit_info_1)
134638918e0bSBorislav Petkov (AMD) return ES_OK;
134738918e0bSBorislav Petkov (AMD)
134838918e0bSBorislav Petkov (AMD) regs->ax = lower_32_bits(this_cpu_read(svsm_caa_pa));
134938918e0bSBorislav Petkov (AMD) regs->dx = upper_32_bits(this_cpu_read(svsm_caa_pa));
135038918e0bSBorislav Petkov (AMD)
135138918e0bSBorislav Petkov (AMD) return ES_OK;
135238918e0bSBorislav Petkov (AMD) }
135338918e0bSBorislav Petkov (AMD)
135438918e0bSBorislav Petkov (AMD) ghcb_set_rcx(ghcb, regs->cx);
135538918e0bSBorislav Petkov (AMD) if (exit_info_1) {
135638918e0bSBorislav Petkov (AMD) ghcb_set_rax(ghcb, regs->ax);
135738918e0bSBorislav Petkov (AMD) ghcb_set_rdx(ghcb, regs->dx);
135838918e0bSBorislav Petkov (AMD) }
135938918e0bSBorislav Petkov (AMD)
136038918e0bSBorislav Petkov (AMD) ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_MSR, exit_info_1, 0);
136138918e0bSBorislav Petkov (AMD)
136238918e0bSBorislav Petkov (AMD) if ((ret == ES_OK) && (!exit_info_1)) {
136338918e0bSBorislav Petkov (AMD) regs->ax = ghcb->save.rax;
136438918e0bSBorislav Petkov (AMD) regs->dx = ghcb->save.rdx;
136538918e0bSBorislav Petkov (AMD) }
136638918e0bSBorislav Petkov (AMD)
136738918e0bSBorislav Petkov (AMD) return ret;
136838918e0bSBorislav Petkov (AMD) }
136938918e0bSBorislav Petkov (AMD)
snp_register_per_cpu_ghcb(void)137038918e0bSBorislav Petkov (AMD) static void snp_register_per_cpu_ghcb(void)
137138918e0bSBorislav Petkov (AMD) {
137238918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data;
137338918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
137438918e0bSBorislav Petkov (AMD)
137538918e0bSBorislav Petkov (AMD) data = this_cpu_read(runtime_data);
137638918e0bSBorislav Petkov (AMD) ghcb = &data->ghcb_page;
137738918e0bSBorislav Petkov (AMD)
137838918e0bSBorislav Petkov (AMD) snp_register_ghcb_early(__pa(ghcb));
137938918e0bSBorislav Petkov (AMD) }
138038918e0bSBorislav Petkov (AMD)
setup_ghcb(void)138138918e0bSBorislav Petkov (AMD) void setup_ghcb(void)
138238918e0bSBorislav Petkov (AMD) {
138338918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
138438918e0bSBorislav Petkov (AMD) return;
138538918e0bSBorislav Petkov (AMD)
138638918e0bSBorislav Petkov (AMD) /*
138738918e0bSBorislav Petkov (AMD) * Check whether the runtime #VC exception handler is active. It uses
138838918e0bSBorislav Petkov (AMD) * the per-CPU GHCB page which is set up by sev_es_init_vc_handling().
138938918e0bSBorislav Petkov (AMD) *
139038918e0bSBorislav Petkov (AMD) * If SNP is active, register the per-CPU GHCB page so that the runtime
139138918e0bSBorislav Petkov (AMD) * exception handler can use it.
139238918e0bSBorislav Petkov (AMD) */
139338918e0bSBorislav Petkov (AMD) if (initial_vc_handler == (unsigned long)kernel_exc_vmm_communication) {
139438918e0bSBorislav Petkov (AMD) if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
139538918e0bSBorislav Petkov (AMD) snp_register_per_cpu_ghcb();
139638918e0bSBorislav Petkov (AMD)
139738918e0bSBorislav Petkov (AMD) sev_cfg.ghcbs_initialized = true;
139838918e0bSBorislav Petkov (AMD)
139938918e0bSBorislav Petkov (AMD) return;
140038918e0bSBorislav Petkov (AMD) }
140138918e0bSBorislav Petkov (AMD)
140238918e0bSBorislav Petkov (AMD) /*
140338918e0bSBorislav Petkov (AMD) * Make sure the hypervisor talks a supported protocol.
140438918e0bSBorislav Petkov (AMD) * This gets called only in the BSP boot phase.
140538918e0bSBorislav Petkov (AMD) */
140638918e0bSBorislav Petkov (AMD) if (!sev_es_negotiate_protocol())
140738918e0bSBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
140838918e0bSBorislav Petkov (AMD)
140938918e0bSBorislav Petkov (AMD) /*
141038918e0bSBorislav Petkov (AMD) * Clear the boot_ghcb. The first exception comes in before the bss
141138918e0bSBorislav Petkov (AMD) * section is cleared.
141238918e0bSBorislav Petkov (AMD) */
141338918e0bSBorislav Petkov (AMD) memset(&boot_ghcb_page, 0, PAGE_SIZE);
141438918e0bSBorislav Petkov (AMD)
141538918e0bSBorislav Petkov (AMD) /* Alright - Make the boot-ghcb public */
141638918e0bSBorislav Petkov (AMD) boot_ghcb = &boot_ghcb_page;
141738918e0bSBorislav Petkov (AMD)
141838918e0bSBorislav Petkov (AMD) /* SNP guest requires that GHCB GPA must be registered. */
141938918e0bSBorislav Petkov (AMD) if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
142038918e0bSBorislav Petkov (AMD) snp_register_ghcb_early(__pa(&boot_ghcb_page));
142138918e0bSBorislav Petkov (AMD) }
142238918e0bSBorislav Petkov (AMD)
142338918e0bSBorislav Petkov (AMD) #ifdef CONFIG_HOTPLUG_CPU
sev_es_ap_hlt_loop(void)142438918e0bSBorislav Petkov (AMD) static void sev_es_ap_hlt_loop(void)
142538918e0bSBorislav Petkov (AMD) {
142638918e0bSBorislav Petkov (AMD) struct ghcb_state state;
142738918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
142838918e0bSBorislav Petkov (AMD)
142938918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
143038918e0bSBorislav Petkov (AMD)
143138918e0bSBorislav Petkov (AMD) while (true) {
143238918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(ghcb);
143338918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_AP_HLT_LOOP);
143438918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_1(ghcb, 0);
143538918e0bSBorislav Petkov (AMD) ghcb_set_sw_exit_info_2(ghcb, 0);
143638918e0bSBorislav Petkov (AMD)
143738918e0bSBorislav Petkov (AMD) sev_es_wr_ghcb_msr(__pa(ghcb));
143838918e0bSBorislav Petkov (AMD) VMGEXIT();
143938918e0bSBorislav Petkov (AMD)
144038918e0bSBorislav Petkov (AMD) /* Wakeup signal? */
144138918e0bSBorislav Petkov (AMD) if (ghcb_sw_exit_info_2_is_valid(ghcb) &&
144238918e0bSBorislav Petkov (AMD) ghcb->save.sw_exit_info_2)
144338918e0bSBorislav Petkov (AMD) break;
144438918e0bSBorislav Petkov (AMD) }
144538918e0bSBorislav Petkov (AMD)
144638918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
144738918e0bSBorislav Petkov (AMD) }
144838918e0bSBorislav Petkov (AMD)
144938918e0bSBorislav Petkov (AMD) /*
145038918e0bSBorislav Petkov (AMD) * Play_dead handler when running under SEV-ES. This is needed because
145138918e0bSBorislav Petkov (AMD) * the hypervisor can't deliver an SIPI request to restart the AP.
145238918e0bSBorislav Petkov (AMD) * Instead the kernel has to issue a VMGEXIT to halt the VCPU until the
145338918e0bSBorislav Petkov (AMD) * hypervisor wakes it up again.
145438918e0bSBorislav Petkov (AMD) */
sev_es_play_dead(void)145538918e0bSBorislav Petkov (AMD) static void sev_es_play_dead(void)
145638918e0bSBorislav Petkov (AMD) {
145738918e0bSBorislav Petkov (AMD) play_dead_common();
145838918e0bSBorislav Petkov (AMD)
145938918e0bSBorislav Petkov (AMD) /* IRQs now disabled */
146038918e0bSBorislav Petkov (AMD)
146138918e0bSBorislav Petkov (AMD) sev_es_ap_hlt_loop();
146238918e0bSBorislav Petkov (AMD)
146338918e0bSBorislav Petkov (AMD) /*
146438918e0bSBorislav Petkov (AMD) * If we get here, the VCPU was woken up again. Jump to CPU
146538918e0bSBorislav Petkov (AMD) * startup code to get it back online.
146638918e0bSBorislav Petkov (AMD) */
146738918e0bSBorislav Petkov (AMD) soft_restart_cpu();
146838918e0bSBorislav Petkov (AMD) }
146938918e0bSBorislav Petkov (AMD) #else /* CONFIG_HOTPLUG_CPU */
147038918e0bSBorislav Petkov (AMD) #define sev_es_play_dead native_play_dead
147138918e0bSBorislav Petkov (AMD) #endif /* CONFIG_HOTPLUG_CPU */
147238918e0bSBorislav Petkov (AMD)
147338918e0bSBorislav Petkov (AMD) #ifdef CONFIG_SMP
sev_es_setup_play_dead(void)147438918e0bSBorislav Petkov (AMD) static void __init sev_es_setup_play_dead(void)
147538918e0bSBorislav Petkov (AMD) {
147638918e0bSBorislav Petkov (AMD) smp_ops.play_dead = sev_es_play_dead;
147738918e0bSBorislav Petkov (AMD) }
147838918e0bSBorislav Petkov (AMD) #else
sev_es_setup_play_dead(void)147938918e0bSBorislav Petkov (AMD) static inline void sev_es_setup_play_dead(void) { }
148038918e0bSBorislav Petkov (AMD) #endif
148138918e0bSBorislav Petkov (AMD)
alloc_runtime_data(int cpu)148238918e0bSBorislav Petkov (AMD) static void __init alloc_runtime_data(int cpu)
148338918e0bSBorislav Petkov (AMD) {
148438918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data;
148538918e0bSBorislav Petkov (AMD)
148638918e0bSBorislav Petkov (AMD) data = memblock_alloc_node(sizeof(*data), PAGE_SIZE, cpu_to_node(cpu));
148738918e0bSBorislav Petkov (AMD) if (!data)
148838918e0bSBorislav Petkov (AMD) panic("Can't allocate SEV-ES runtime data");
148938918e0bSBorislav Petkov (AMD)
149038918e0bSBorislav Petkov (AMD) per_cpu(runtime_data, cpu) = data;
149138918e0bSBorislav Petkov (AMD)
149238918e0bSBorislav Petkov (AMD) if (snp_vmpl) {
149338918e0bSBorislav Petkov (AMD) struct svsm_ca *caa;
149438918e0bSBorislav Petkov (AMD)
149538918e0bSBorislav Petkov (AMD) /* Allocate the SVSM CA page if an SVSM is present */
149638918e0bSBorislav Petkov (AMD) caa = memblock_alloc(sizeof(*caa), PAGE_SIZE);
149738918e0bSBorislav Petkov (AMD) if (!caa)
149838918e0bSBorislav Petkov (AMD) panic("Can't allocate SVSM CA page\n");
149938918e0bSBorislav Petkov (AMD)
150038918e0bSBorislav Petkov (AMD) per_cpu(svsm_caa, cpu) = caa;
150138918e0bSBorislav Petkov (AMD) per_cpu(svsm_caa_pa, cpu) = __pa(caa);
150238918e0bSBorislav Petkov (AMD) }
150338918e0bSBorislav Petkov (AMD) }
150438918e0bSBorislav Petkov (AMD)
init_ghcb(int cpu)150538918e0bSBorislav Petkov (AMD) static void __init init_ghcb(int cpu)
150638918e0bSBorislav Petkov (AMD) {
150738918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data;
150838918e0bSBorislav Petkov (AMD) int err;
150938918e0bSBorislav Petkov (AMD)
151038918e0bSBorislav Petkov (AMD) data = per_cpu(runtime_data, cpu);
151138918e0bSBorislav Petkov (AMD)
151238918e0bSBorislav Petkov (AMD) err = early_set_memory_decrypted((unsigned long)&data->ghcb_page,
151338918e0bSBorislav Petkov (AMD) sizeof(data->ghcb_page));
151438918e0bSBorislav Petkov (AMD) if (err)
151538918e0bSBorislav Petkov (AMD) panic("Can't map GHCBs unencrypted");
151638918e0bSBorislav Petkov (AMD)
151738918e0bSBorislav Petkov (AMD) memset(&data->ghcb_page, 0, sizeof(data->ghcb_page));
151838918e0bSBorislav Petkov (AMD)
151938918e0bSBorislav Petkov (AMD) data->ghcb_active = false;
152038918e0bSBorislav Petkov (AMD) data->backup_ghcb_active = false;
152138918e0bSBorislav Petkov (AMD) }
152238918e0bSBorislav Petkov (AMD)
sev_es_init_vc_handling(void)152338918e0bSBorislav Petkov (AMD) void __init sev_es_init_vc_handling(void)
152438918e0bSBorislav Petkov (AMD) {
152538918e0bSBorislav Petkov (AMD) int cpu;
152638918e0bSBorislav Petkov (AMD)
152738918e0bSBorislav Petkov (AMD) BUILD_BUG_ON(offsetof(struct sev_es_runtime_data, ghcb_page) % PAGE_SIZE);
152838918e0bSBorislav Petkov (AMD)
152938918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
153038918e0bSBorislav Petkov (AMD) return;
153138918e0bSBorislav Petkov (AMD)
153238918e0bSBorislav Petkov (AMD) if (!sev_es_check_cpu_features())
153338918e0bSBorislav Petkov (AMD) panic("SEV-ES CPU Features missing");
153438918e0bSBorislav Petkov (AMD)
153538918e0bSBorislav Petkov (AMD) /*
153638918e0bSBorislav Petkov (AMD) * SNP is supported in v2 of the GHCB spec which mandates support for HV
153738918e0bSBorislav Petkov (AMD) * features.
153838918e0bSBorislav Petkov (AMD) */
153938918e0bSBorislav Petkov (AMD) if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
154038918e0bSBorislav Petkov (AMD) sev_hv_features = get_hv_features();
154138918e0bSBorislav Petkov (AMD)
154238918e0bSBorislav Petkov (AMD) if (!(sev_hv_features & GHCB_HV_FT_SNP))
154338918e0bSBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
154438918e0bSBorislav Petkov (AMD) }
154538918e0bSBorislav Petkov (AMD)
154638918e0bSBorislav Petkov (AMD) /* Initialize per-cpu GHCB pages */
154738918e0bSBorislav Petkov (AMD) for_each_possible_cpu(cpu) {
154838918e0bSBorislav Petkov (AMD) alloc_runtime_data(cpu);
154938918e0bSBorislav Petkov (AMD) init_ghcb(cpu);
155038918e0bSBorislav Petkov (AMD) }
155138918e0bSBorislav Petkov (AMD)
155238918e0bSBorislav Petkov (AMD) /* If running under an SVSM, switch to the per-cpu CA */
155338918e0bSBorislav Petkov (AMD) if (snp_vmpl) {
155438918e0bSBorislav Petkov (AMD) struct svsm_call call = {};
155538918e0bSBorislav Petkov (AMD) unsigned long flags;
155638918e0bSBorislav Petkov (AMD) int ret;
155738918e0bSBorislav Petkov (AMD)
155838918e0bSBorislav Petkov (AMD) local_irq_save(flags);
155938918e0bSBorislav Petkov (AMD)
156038918e0bSBorislav Petkov (AMD) /*
156138918e0bSBorislav Petkov (AMD) * SVSM_CORE_REMAP_CA call:
156238918e0bSBorislav Petkov (AMD) * RAX = 0 (Protocol=0, CallID=0)
156338918e0bSBorislav Petkov (AMD) * RCX = New CA GPA
156438918e0bSBorislav Petkov (AMD) */
156538918e0bSBorislav Petkov (AMD) call.caa = svsm_get_caa();
156638918e0bSBorislav Petkov (AMD) call.rax = SVSM_CORE_CALL(SVSM_CORE_REMAP_CA);
156738918e0bSBorislav Petkov (AMD) call.rcx = this_cpu_read(svsm_caa_pa);
156838918e0bSBorislav Petkov (AMD) ret = svsm_perform_call_protocol(&call);
156938918e0bSBorislav Petkov (AMD) if (ret)
157038918e0bSBorislav Petkov (AMD) panic("Can't remap the SVSM CA, ret=%d, rax_out=0x%llx\n",
157138918e0bSBorislav Petkov (AMD) ret, call.rax_out);
157238918e0bSBorislav Petkov (AMD)
157338918e0bSBorislav Petkov (AMD) sev_cfg.use_cas = true;
157438918e0bSBorislav Petkov (AMD)
157538918e0bSBorislav Petkov (AMD) local_irq_restore(flags);
157638918e0bSBorislav Petkov (AMD) }
157738918e0bSBorislav Petkov (AMD)
157838918e0bSBorislav Petkov (AMD) sev_es_setup_play_dead();
157938918e0bSBorislav Petkov (AMD)
158038918e0bSBorislav Petkov (AMD) /* Secondary CPUs use the runtime #VC handler */
158138918e0bSBorislav Petkov (AMD) initial_vc_handler = (unsigned long)kernel_exc_vmm_communication;
158238918e0bSBorislav Petkov (AMD) }
158338918e0bSBorislav Petkov (AMD)
vc_early_forward_exception(struct es_em_ctxt * ctxt)158438918e0bSBorislav Petkov (AMD) static void __init vc_early_forward_exception(struct es_em_ctxt *ctxt)
158538918e0bSBorislav Petkov (AMD) {
158638918e0bSBorislav Petkov (AMD) int trapnr = ctxt->fi.vector;
158738918e0bSBorislav Petkov (AMD)
158838918e0bSBorislav Petkov (AMD) if (trapnr == X86_TRAP_PF)
158938918e0bSBorislav Petkov (AMD) native_write_cr2(ctxt->fi.cr2);
159038918e0bSBorislav Petkov (AMD)
159138918e0bSBorislav Petkov (AMD) ctxt->regs->orig_ax = ctxt->fi.error_code;
159238918e0bSBorislav Petkov (AMD) do_early_exception(ctxt->regs, trapnr);
159338918e0bSBorislav Petkov (AMD) }
159438918e0bSBorislav Petkov (AMD)
vc_insn_get_rm(struct es_em_ctxt * ctxt)159538918e0bSBorislav Petkov (AMD) static long *vc_insn_get_rm(struct es_em_ctxt *ctxt)
159638918e0bSBorislav Petkov (AMD) {
159738918e0bSBorislav Petkov (AMD) long *reg_array;
159838918e0bSBorislav Petkov (AMD) int offset;
159938918e0bSBorislav Petkov (AMD)
160038918e0bSBorislav Petkov (AMD) reg_array = (long *)ctxt->regs;
160138918e0bSBorislav Petkov (AMD) offset = insn_get_modrm_rm_off(&ctxt->insn, ctxt->regs);
160238918e0bSBorislav Petkov (AMD)
160338918e0bSBorislav Petkov (AMD) if (offset < 0)
160438918e0bSBorislav Petkov (AMD) return NULL;
160538918e0bSBorislav Petkov (AMD)
160638918e0bSBorislav Petkov (AMD) offset /= sizeof(long);
160738918e0bSBorislav Petkov (AMD)
160838918e0bSBorislav Petkov (AMD) return reg_array + offset;
160938918e0bSBorislav Petkov (AMD) }
vc_do_mmio(struct ghcb * ghcb,struct es_em_ctxt * ctxt,unsigned int bytes,bool read)161038918e0bSBorislav Petkov (AMD) static enum es_result vc_do_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt,
161138918e0bSBorislav Petkov (AMD) unsigned int bytes, bool read)
161238918e0bSBorislav Petkov (AMD) {
161338918e0bSBorislav Petkov (AMD) u64 exit_code, exit_info_1, exit_info_2;
161438918e0bSBorislav Petkov (AMD) unsigned long ghcb_pa = __pa(ghcb);
161538918e0bSBorislav Petkov (AMD) enum es_result res;
161638918e0bSBorislav Petkov (AMD) phys_addr_t paddr;
161738918e0bSBorislav Petkov (AMD) void __user *ref;
161838918e0bSBorislav Petkov (AMD)
161938918e0bSBorislav Petkov (AMD) ref = insn_get_addr_ref(&ctxt->insn, ctxt->regs);
162038918e0bSBorislav Petkov (AMD) if (ref == (void __user *)-1L)
162138918e0bSBorislav Petkov (AMD) return ES_UNSUPPORTED;
162238918e0bSBorislav Petkov (AMD)
162338918e0bSBorislav Petkov (AMD) exit_code = read ? SVM_VMGEXIT_MMIO_READ : SVM_VMGEXIT_MMIO_WRITE;
162438918e0bSBorislav Petkov (AMD)
162538918e0bSBorislav Petkov (AMD) res = vc_slow_virt_to_phys(ghcb, ctxt, (unsigned long)ref, &paddr);
162638918e0bSBorislav Petkov (AMD) if (res != ES_OK) {
162738918e0bSBorislav Petkov (AMD) if (res == ES_EXCEPTION && !read)
162838918e0bSBorislav Petkov (AMD) ctxt->fi.error_code |= X86_PF_WRITE;
162938918e0bSBorislav Petkov (AMD)
163038918e0bSBorislav Petkov (AMD) return res;
163138918e0bSBorislav Petkov (AMD) }
163238918e0bSBorislav Petkov (AMD)
163338918e0bSBorislav Petkov (AMD) exit_info_1 = paddr;
163438918e0bSBorislav Petkov (AMD) /* Can never be greater than 8 */
163538918e0bSBorislav Petkov (AMD) exit_info_2 = bytes;
163638918e0bSBorislav Petkov (AMD)
163738918e0bSBorislav Petkov (AMD) ghcb_set_sw_scratch(ghcb, ghcb_pa + offsetof(struct ghcb, shared_buffer));
163838918e0bSBorislav Petkov (AMD)
163938918e0bSBorislav Petkov (AMD) return sev_es_ghcb_hv_call(ghcb, ctxt, exit_code, exit_info_1, exit_info_2);
164038918e0bSBorislav Petkov (AMD) }
164138918e0bSBorislav Petkov (AMD)
164238918e0bSBorislav Petkov (AMD) /*
164338918e0bSBorislav Petkov (AMD) * The MOVS instruction has two memory operands, which raises the
164438918e0bSBorislav Petkov (AMD) * problem that it is not known whether the access to the source or the
164538918e0bSBorislav Petkov (AMD) * destination caused the #VC exception (and hence whether an MMIO read
164638918e0bSBorislav Petkov (AMD) * or write operation needs to be emulated).
164738918e0bSBorislav Petkov (AMD) *
164838918e0bSBorislav Petkov (AMD) * Instead of playing games with walking page-tables and trying to guess
164938918e0bSBorislav Petkov (AMD) * whether the source or destination is an MMIO range, split the move
165038918e0bSBorislav Petkov (AMD) * into two operations, a read and a write with only one memory operand.
165138918e0bSBorislav Petkov (AMD) * This will cause a nested #VC exception on the MMIO address which can
165238918e0bSBorislav Petkov (AMD) * then be handled.
165338918e0bSBorislav Petkov (AMD) *
165438918e0bSBorislav Petkov (AMD) * This implementation has the benefit that it also supports MOVS where
165538918e0bSBorislav Petkov (AMD) * source _and_ destination are MMIO regions.
165638918e0bSBorislav Petkov (AMD) *
165738918e0bSBorislav Petkov (AMD) * It will slow MOVS on MMIO down a lot, but in SEV-ES guests it is a
165838918e0bSBorislav Petkov (AMD) * rare operation. If it turns out to be a performance problem the split
165938918e0bSBorislav Petkov (AMD) * operations can be moved to memcpy_fromio() and memcpy_toio().
166038918e0bSBorislav Petkov (AMD) */
vc_handle_mmio_movs(struct es_em_ctxt * ctxt,unsigned int bytes)166138918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_mmio_movs(struct es_em_ctxt *ctxt,
166238918e0bSBorislav Petkov (AMD) unsigned int bytes)
166338918e0bSBorislav Petkov (AMD) {
166438918e0bSBorislav Petkov (AMD) unsigned long ds_base, es_base;
166538918e0bSBorislav Petkov (AMD) unsigned char *src, *dst;
166638918e0bSBorislav Petkov (AMD) unsigned char buffer[8];
166738918e0bSBorislav Petkov (AMD) enum es_result ret;
166838918e0bSBorislav Petkov (AMD) bool rep;
166938918e0bSBorislav Petkov (AMD) int off;
167038918e0bSBorislav Petkov (AMD)
167138918e0bSBorislav Petkov (AMD) ds_base = insn_get_seg_base(ctxt->regs, INAT_SEG_REG_DS);
167238918e0bSBorislav Petkov (AMD) es_base = insn_get_seg_base(ctxt->regs, INAT_SEG_REG_ES);
167338918e0bSBorislav Petkov (AMD)
167438918e0bSBorislav Petkov (AMD) if (ds_base == -1L || es_base == -1L) {
167538918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_GP;
167638918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = 0;
167738918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
167838918e0bSBorislav Petkov (AMD) }
167938918e0bSBorislav Petkov (AMD)
168038918e0bSBorislav Petkov (AMD) src = ds_base + (unsigned char *)ctxt->regs->si;
168138918e0bSBorislav Petkov (AMD) dst = es_base + (unsigned char *)ctxt->regs->di;
168238918e0bSBorislav Petkov (AMD)
168338918e0bSBorislav Petkov (AMD) ret = vc_read_mem(ctxt, src, buffer, bytes);
168438918e0bSBorislav Petkov (AMD) if (ret != ES_OK)
168538918e0bSBorislav Petkov (AMD) return ret;
168638918e0bSBorislav Petkov (AMD)
168738918e0bSBorislav Petkov (AMD) ret = vc_write_mem(ctxt, dst, buffer, bytes);
168838918e0bSBorislav Petkov (AMD) if (ret != ES_OK)
168938918e0bSBorislav Petkov (AMD) return ret;
169038918e0bSBorislav Petkov (AMD)
169138918e0bSBorislav Petkov (AMD) if (ctxt->regs->flags & X86_EFLAGS_DF)
169238918e0bSBorislav Petkov (AMD) off = -bytes;
169338918e0bSBorislav Petkov (AMD) else
169438918e0bSBorislav Petkov (AMD) off = bytes;
169538918e0bSBorislav Petkov (AMD)
169638918e0bSBorislav Petkov (AMD) ctxt->regs->si += off;
169738918e0bSBorislav Petkov (AMD) ctxt->regs->di += off;
169838918e0bSBorislav Petkov (AMD)
169938918e0bSBorislav Petkov (AMD) rep = insn_has_rep_prefix(&ctxt->insn);
170038918e0bSBorislav Petkov (AMD) if (rep)
170138918e0bSBorislav Petkov (AMD) ctxt->regs->cx -= 1;
170238918e0bSBorislav Petkov (AMD)
170338918e0bSBorislav Petkov (AMD) if (!rep || ctxt->regs->cx == 0)
170438918e0bSBorislav Petkov (AMD) return ES_OK;
170538918e0bSBorislav Petkov (AMD) else
170638918e0bSBorislav Petkov (AMD) return ES_RETRY;
170738918e0bSBorislav Petkov (AMD) }
170838918e0bSBorislav Petkov (AMD)
vc_handle_mmio(struct ghcb * ghcb,struct es_em_ctxt * ctxt)170938918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
171038918e0bSBorislav Petkov (AMD) {
171138918e0bSBorislav Petkov (AMD) struct insn *insn = &ctxt->insn;
171238918e0bSBorislav Petkov (AMD) enum insn_mmio_type mmio;
171338918e0bSBorislav Petkov (AMD) unsigned int bytes = 0;
171438918e0bSBorislav Petkov (AMD) enum es_result ret;
171538918e0bSBorislav Petkov (AMD) u8 sign_byte;
171638918e0bSBorislav Petkov (AMD) long *reg_data;
171738918e0bSBorislav Petkov (AMD)
171838918e0bSBorislav Petkov (AMD) mmio = insn_decode_mmio(insn, &bytes);
171938918e0bSBorislav Petkov (AMD) if (mmio == INSN_MMIO_DECODE_FAILED)
172038918e0bSBorislav Petkov (AMD) return ES_DECODE_FAILED;
172138918e0bSBorislav Petkov (AMD)
172238918e0bSBorislav Petkov (AMD) if (mmio != INSN_MMIO_WRITE_IMM && mmio != INSN_MMIO_MOVS) {
172338918e0bSBorislav Petkov (AMD) reg_data = insn_get_modrm_reg_ptr(insn, ctxt->regs);
172438918e0bSBorislav Petkov (AMD) if (!reg_data)
172538918e0bSBorislav Petkov (AMD) return ES_DECODE_FAILED;
172638918e0bSBorislav Petkov (AMD) }
172738918e0bSBorislav Petkov (AMD)
172838918e0bSBorislav Petkov (AMD) if (user_mode(ctxt->regs))
172938918e0bSBorislav Petkov (AMD) return ES_UNSUPPORTED;
173038918e0bSBorislav Petkov (AMD)
173138918e0bSBorislav Petkov (AMD) switch (mmio) {
173238918e0bSBorislav Petkov (AMD) case INSN_MMIO_WRITE:
173338918e0bSBorislav Petkov (AMD) memcpy(ghcb->shared_buffer, reg_data, bytes);
173438918e0bSBorislav Petkov (AMD) ret = vc_do_mmio(ghcb, ctxt, bytes, false);
173538918e0bSBorislav Petkov (AMD) break;
173638918e0bSBorislav Petkov (AMD) case INSN_MMIO_WRITE_IMM:
173738918e0bSBorislav Petkov (AMD) memcpy(ghcb->shared_buffer, insn->immediate1.bytes, bytes);
173838918e0bSBorislav Petkov (AMD) ret = vc_do_mmio(ghcb, ctxt, bytes, false);
173938918e0bSBorislav Petkov (AMD) break;
174038918e0bSBorislav Petkov (AMD) case INSN_MMIO_READ:
174138918e0bSBorislav Petkov (AMD) ret = vc_do_mmio(ghcb, ctxt, bytes, true);
174238918e0bSBorislav Petkov (AMD) if (ret)
174338918e0bSBorislav Petkov (AMD) break;
174438918e0bSBorislav Petkov (AMD)
174538918e0bSBorislav Petkov (AMD) /* Zero-extend for 32-bit operation */
174638918e0bSBorislav Petkov (AMD) if (bytes == 4)
174738918e0bSBorislav Petkov (AMD) *reg_data = 0;
174838918e0bSBorislav Petkov (AMD)
174938918e0bSBorislav Petkov (AMD) memcpy(reg_data, ghcb->shared_buffer, bytes);
175038918e0bSBorislav Petkov (AMD) break;
175138918e0bSBorislav Petkov (AMD) case INSN_MMIO_READ_ZERO_EXTEND:
175238918e0bSBorislav Petkov (AMD) ret = vc_do_mmio(ghcb, ctxt, bytes, true);
175338918e0bSBorislav Petkov (AMD) if (ret)
175438918e0bSBorislav Petkov (AMD) break;
175538918e0bSBorislav Petkov (AMD)
175638918e0bSBorislav Petkov (AMD) /* Zero extend based on operand size */
175738918e0bSBorislav Petkov (AMD) memset(reg_data, 0, insn->opnd_bytes);
175838918e0bSBorislav Petkov (AMD) memcpy(reg_data, ghcb->shared_buffer, bytes);
175938918e0bSBorislav Petkov (AMD) break;
176038918e0bSBorislav Petkov (AMD) case INSN_MMIO_READ_SIGN_EXTEND:
176138918e0bSBorislav Petkov (AMD) ret = vc_do_mmio(ghcb, ctxt, bytes, true);
176238918e0bSBorislav Petkov (AMD) if (ret)
176338918e0bSBorislav Petkov (AMD) break;
176438918e0bSBorislav Petkov (AMD)
176538918e0bSBorislav Petkov (AMD) if (bytes == 1) {
176638918e0bSBorislav Petkov (AMD) u8 *val = (u8 *)ghcb->shared_buffer;
176738918e0bSBorislav Petkov (AMD)
176838918e0bSBorislav Petkov (AMD) sign_byte = (*val & 0x80) ? 0xff : 0x00;
176938918e0bSBorislav Petkov (AMD) } else {
177038918e0bSBorislav Petkov (AMD) u16 *val = (u16 *)ghcb->shared_buffer;
177138918e0bSBorislav Petkov (AMD)
177238918e0bSBorislav Petkov (AMD) sign_byte = (*val & 0x8000) ? 0xff : 0x00;
177338918e0bSBorislav Petkov (AMD) }
177438918e0bSBorislav Petkov (AMD)
177538918e0bSBorislav Petkov (AMD) /* Sign extend based on operand size */
177638918e0bSBorislav Petkov (AMD) memset(reg_data, sign_byte, insn->opnd_bytes);
177738918e0bSBorislav Petkov (AMD) memcpy(reg_data, ghcb->shared_buffer, bytes);
177838918e0bSBorislav Petkov (AMD) break;
177938918e0bSBorislav Petkov (AMD) case INSN_MMIO_MOVS:
178038918e0bSBorislav Petkov (AMD) ret = vc_handle_mmio_movs(ctxt, bytes);
178138918e0bSBorislav Petkov (AMD) break;
178238918e0bSBorislav Petkov (AMD) default:
178338918e0bSBorislav Petkov (AMD) ret = ES_UNSUPPORTED;
178438918e0bSBorislav Petkov (AMD) break;
178538918e0bSBorislav Petkov (AMD) }
178638918e0bSBorislav Petkov (AMD)
178738918e0bSBorislav Petkov (AMD) return ret;
178838918e0bSBorislav Petkov (AMD) }
178938918e0bSBorislav Petkov (AMD)
vc_handle_dr7_write(struct ghcb * ghcb,struct es_em_ctxt * ctxt)179038918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_dr7_write(struct ghcb *ghcb,
179138918e0bSBorislav Petkov (AMD) struct es_em_ctxt *ctxt)
179238918e0bSBorislav Petkov (AMD) {
179338918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data = this_cpu_read(runtime_data);
179438918e0bSBorislav Petkov (AMD) long val, *reg = vc_insn_get_rm(ctxt);
179538918e0bSBorislav Petkov (AMD) enum es_result ret;
179638918e0bSBorislav Petkov (AMD)
179738918e0bSBorislav Petkov (AMD) if (sev_status & MSR_AMD64_SNP_DEBUG_SWAP)
179838918e0bSBorislav Petkov (AMD) return ES_VMM_ERROR;
179938918e0bSBorislav Petkov (AMD)
180038918e0bSBorislav Petkov (AMD) if (!reg)
180138918e0bSBorislav Petkov (AMD) return ES_DECODE_FAILED;
180238918e0bSBorislav Petkov (AMD)
180338918e0bSBorislav Petkov (AMD) val = *reg;
180438918e0bSBorislav Petkov (AMD)
180538918e0bSBorislav Petkov (AMD) /* Upper 32 bits must be written as zeroes */
180638918e0bSBorislav Petkov (AMD) if (val >> 32) {
180738918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_GP;
180838918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = 0;
180938918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
181038918e0bSBorislav Petkov (AMD) }
181138918e0bSBorislav Petkov (AMD)
181238918e0bSBorislav Petkov (AMD) /* Clear out other reserved bits and set bit 10 */
181338918e0bSBorislav Petkov (AMD) val = (val & 0xffff23ffL) | BIT(10);
181438918e0bSBorislav Petkov (AMD)
181538918e0bSBorislav Petkov (AMD) /* Early non-zero writes to DR7 are not supported */
181638918e0bSBorislav Petkov (AMD) if (!data && (val & ~DR7_RESET_VALUE))
181738918e0bSBorislav Petkov (AMD) return ES_UNSUPPORTED;
181838918e0bSBorislav Petkov (AMD)
181938918e0bSBorislav Petkov (AMD) /* Using a value of 0 for ExitInfo1 means RAX holds the value */
182038918e0bSBorislav Petkov (AMD) ghcb_set_rax(ghcb, val);
182138918e0bSBorislav Petkov (AMD) ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_WRITE_DR7, 0, 0);
182238918e0bSBorislav Petkov (AMD) if (ret != ES_OK)
182338918e0bSBorislav Petkov (AMD) return ret;
182438918e0bSBorislav Petkov (AMD)
182538918e0bSBorislav Petkov (AMD) if (data)
182638918e0bSBorislav Petkov (AMD) data->dr7 = val;
182738918e0bSBorislav Petkov (AMD)
182838918e0bSBorislav Petkov (AMD) return ES_OK;
182938918e0bSBorislav Petkov (AMD) }
183038918e0bSBorislav Petkov (AMD)
vc_handle_dr7_read(struct ghcb * ghcb,struct es_em_ctxt * ctxt)183138918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_dr7_read(struct ghcb *ghcb,
183238918e0bSBorislav Petkov (AMD) struct es_em_ctxt *ctxt)
183338918e0bSBorislav Petkov (AMD) {
183438918e0bSBorislav Petkov (AMD) struct sev_es_runtime_data *data = this_cpu_read(runtime_data);
183538918e0bSBorislav Petkov (AMD) long *reg = vc_insn_get_rm(ctxt);
183638918e0bSBorislav Petkov (AMD)
183738918e0bSBorislav Petkov (AMD) if (sev_status & MSR_AMD64_SNP_DEBUG_SWAP)
183838918e0bSBorislav Petkov (AMD) return ES_VMM_ERROR;
183938918e0bSBorislav Petkov (AMD)
184038918e0bSBorislav Petkov (AMD) if (!reg)
184138918e0bSBorislav Petkov (AMD) return ES_DECODE_FAILED;
184238918e0bSBorislav Petkov (AMD)
184338918e0bSBorislav Petkov (AMD) if (data)
184438918e0bSBorislav Petkov (AMD) *reg = data->dr7;
184538918e0bSBorislav Petkov (AMD) else
184638918e0bSBorislav Petkov (AMD) *reg = DR7_RESET_VALUE;
184738918e0bSBorislav Petkov (AMD)
184838918e0bSBorislav Petkov (AMD) return ES_OK;
184938918e0bSBorislav Petkov (AMD) }
185038918e0bSBorislav Petkov (AMD)
vc_handle_wbinvd(struct ghcb * ghcb,struct es_em_ctxt * ctxt)185138918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_wbinvd(struct ghcb *ghcb,
185238918e0bSBorislav Petkov (AMD) struct es_em_ctxt *ctxt)
185338918e0bSBorislav Petkov (AMD) {
185438918e0bSBorislav Petkov (AMD) return sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_WBINVD, 0, 0);
185538918e0bSBorislav Petkov (AMD) }
185638918e0bSBorislav Petkov (AMD)
vc_handle_rdpmc(struct ghcb * ghcb,struct es_em_ctxt * ctxt)185738918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_rdpmc(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
185838918e0bSBorislav Petkov (AMD) {
185938918e0bSBorislav Petkov (AMD) enum es_result ret;
186038918e0bSBorislav Petkov (AMD)
186138918e0bSBorislav Petkov (AMD) ghcb_set_rcx(ghcb, ctxt->regs->cx);
186238918e0bSBorislav Petkov (AMD)
186338918e0bSBorislav Petkov (AMD) ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_RDPMC, 0, 0);
186438918e0bSBorislav Petkov (AMD) if (ret != ES_OK)
186538918e0bSBorislav Petkov (AMD) return ret;
186638918e0bSBorislav Petkov (AMD)
186738918e0bSBorislav Petkov (AMD) if (!(ghcb_rax_is_valid(ghcb) && ghcb_rdx_is_valid(ghcb)))
186838918e0bSBorislav Petkov (AMD) return ES_VMM_ERROR;
186938918e0bSBorislav Petkov (AMD)
187038918e0bSBorislav Petkov (AMD) ctxt->regs->ax = ghcb->save.rax;
187138918e0bSBorislav Petkov (AMD) ctxt->regs->dx = ghcb->save.rdx;
187238918e0bSBorislav Petkov (AMD)
187338918e0bSBorislav Petkov (AMD) return ES_OK;
187438918e0bSBorislav Petkov (AMD) }
187538918e0bSBorislav Petkov (AMD)
vc_handle_monitor(struct ghcb * ghcb,struct es_em_ctxt * ctxt)187638918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_monitor(struct ghcb *ghcb,
187738918e0bSBorislav Petkov (AMD) struct es_em_ctxt *ctxt)
187838918e0bSBorislav Petkov (AMD) {
187938918e0bSBorislav Petkov (AMD) /*
188038918e0bSBorislav Petkov (AMD) * Treat it as a NOP and do not leak a physical address to the
188138918e0bSBorislav Petkov (AMD) * hypervisor.
188238918e0bSBorislav Petkov (AMD) */
188338918e0bSBorislav Petkov (AMD) return ES_OK;
188438918e0bSBorislav Petkov (AMD) }
188538918e0bSBorislav Petkov (AMD)
vc_handle_mwait(struct ghcb * ghcb,struct es_em_ctxt * ctxt)188638918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_mwait(struct ghcb *ghcb,
188738918e0bSBorislav Petkov (AMD) struct es_em_ctxt *ctxt)
188838918e0bSBorislav Petkov (AMD) {
188938918e0bSBorislav Petkov (AMD) /* Treat the same as MONITOR/MONITORX */
189038918e0bSBorislav Petkov (AMD) return ES_OK;
189138918e0bSBorislav Petkov (AMD) }
189238918e0bSBorislav Petkov (AMD)
vc_handle_vmmcall(struct ghcb * ghcb,struct es_em_ctxt * ctxt)189338918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_vmmcall(struct ghcb *ghcb,
189438918e0bSBorislav Petkov (AMD) struct es_em_ctxt *ctxt)
189538918e0bSBorislav Petkov (AMD) {
189638918e0bSBorislav Petkov (AMD) enum es_result ret;
189738918e0bSBorislav Petkov (AMD)
189838918e0bSBorislav Petkov (AMD) ghcb_set_rax(ghcb, ctxt->regs->ax);
189938918e0bSBorislav Petkov (AMD) ghcb_set_cpl(ghcb, user_mode(ctxt->regs) ? 3 : 0);
190038918e0bSBorislav Petkov (AMD)
190138918e0bSBorislav Petkov (AMD) if (x86_platform.hyper.sev_es_hcall_prepare)
190238918e0bSBorislav Petkov (AMD) x86_platform.hyper.sev_es_hcall_prepare(ghcb, ctxt->regs);
190338918e0bSBorislav Petkov (AMD)
190438918e0bSBorislav Petkov (AMD) ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_VMMCALL, 0, 0);
190538918e0bSBorislav Petkov (AMD) if (ret != ES_OK)
190638918e0bSBorislav Petkov (AMD) return ret;
190738918e0bSBorislav Petkov (AMD)
190838918e0bSBorislav Petkov (AMD) if (!ghcb_rax_is_valid(ghcb))
190938918e0bSBorislav Petkov (AMD) return ES_VMM_ERROR;
191038918e0bSBorislav Petkov (AMD)
191138918e0bSBorislav Petkov (AMD) ctxt->regs->ax = ghcb->save.rax;
191238918e0bSBorislav Petkov (AMD)
191338918e0bSBorislav Petkov (AMD) /*
191438918e0bSBorislav Petkov (AMD) * Call sev_es_hcall_finish() after regs->ax is already set.
191538918e0bSBorislav Petkov (AMD) * This allows the hypervisor handler to overwrite it again if
191638918e0bSBorislav Petkov (AMD) * necessary.
191738918e0bSBorislav Petkov (AMD) */
191838918e0bSBorislav Petkov (AMD) if (x86_platform.hyper.sev_es_hcall_finish &&
191938918e0bSBorislav Petkov (AMD) !x86_platform.hyper.sev_es_hcall_finish(ghcb, ctxt->regs))
192038918e0bSBorislav Petkov (AMD) return ES_VMM_ERROR;
192138918e0bSBorislav Petkov (AMD)
192238918e0bSBorislav Petkov (AMD) return ES_OK;
192338918e0bSBorislav Petkov (AMD) }
192438918e0bSBorislav Petkov (AMD)
vc_handle_trap_ac(struct ghcb * ghcb,struct es_em_ctxt * ctxt)192538918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_trap_ac(struct ghcb *ghcb,
192638918e0bSBorislav Petkov (AMD) struct es_em_ctxt *ctxt)
192738918e0bSBorislav Petkov (AMD) {
192838918e0bSBorislav Petkov (AMD) /*
192938918e0bSBorislav Petkov (AMD) * Calling ecx_alignment_check() directly does not work, because it
193038918e0bSBorislav Petkov (AMD) * enables IRQs and the GHCB is active. Forward the exception and call
193138918e0bSBorislav Petkov (AMD) * it later from vc_forward_exception().
193238918e0bSBorislav Petkov (AMD) */
193338918e0bSBorislav Petkov (AMD) ctxt->fi.vector = X86_TRAP_AC;
193438918e0bSBorislav Petkov (AMD) ctxt->fi.error_code = 0;
193538918e0bSBorislav Petkov (AMD) return ES_EXCEPTION;
193638918e0bSBorislav Petkov (AMD) }
193738918e0bSBorislav Petkov (AMD)
vc_handle_exitcode(struct es_em_ctxt * ctxt,struct ghcb * ghcb,unsigned long exit_code)193838918e0bSBorislav Petkov (AMD) static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt,
193938918e0bSBorislav Petkov (AMD) struct ghcb *ghcb,
194038918e0bSBorislav Petkov (AMD) unsigned long exit_code)
194138918e0bSBorislav Petkov (AMD) {
194238918e0bSBorislav Petkov (AMD) enum es_result result = vc_check_opcode_bytes(ctxt, exit_code);
194338918e0bSBorislav Petkov (AMD)
194438918e0bSBorislav Petkov (AMD) if (result != ES_OK)
194538918e0bSBorislav Petkov (AMD) return result;
194638918e0bSBorislav Petkov (AMD)
194738918e0bSBorislav Petkov (AMD) switch (exit_code) {
194838918e0bSBorislav Petkov (AMD) case SVM_EXIT_READ_DR7:
194938918e0bSBorislav Petkov (AMD) result = vc_handle_dr7_read(ghcb, ctxt);
195038918e0bSBorislav Petkov (AMD) break;
195138918e0bSBorislav Petkov (AMD) case SVM_EXIT_WRITE_DR7:
195238918e0bSBorislav Petkov (AMD) result = vc_handle_dr7_write(ghcb, ctxt);
195338918e0bSBorislav Petkov (AMD) break;
195438918e0bSBorislav Petkov (AMD) case SVM_EXIT_EXCP_BASE + X86_TRAP_AC:
195538918e0bSBorislav Petkov (AMD) result = vc_handle_trap_ac(ghcb, ctxt);
195638918e0bSBorislav Petkov (AMD) break;
195738918e0bSBorislav Petkov (AMD) case SVM_EXIT_RDTSC:
195838918e0bSBorislav Petkov (AMD) case SVM_EXIT_RDTSCP:
195938918e0bSBorislav Petkov (AMD) result = vc_handle_rdtsc(ghcb, ctxt, exit_code);
196038918e0bSBorislav Petkov (AMD) break;
196138918e0bSBorislav Petkov (AMD) case SVM_EXIT_RDPMC:
196238918e0bSBorislav Petkov (AMD) result = vc_handle_rdpmc(ghcb, ctxt);
196338918e0bSBorislav Petkov (AMD) break;
196438918e0bSBorislav Petkov (AMD) case SVM_EXIT_INVD:
196538918e0bSBorislav Petkov (AMD) pr_err_ratelimited("#VC exception for INVD??? Seriously???\n");
196638918e0bSBorislav Petkov (AMD) result = ES_UNSUPPORTED;
196738918e0bSBorislav Petkov (AMD) break;
196838918e0bSBorislav Petkov (AMD) case SVM_EXIT_CPUID:
196938918e0bSBorislav Petkov (AMD) result = vc_handle_cpuid(ghcb, ctxt);
197038918e0bSBorislav Petkov (AMD) break;
197138918e0bSBorislav Petkov (AMD) case SVM_EXIT_IOIO:
197238918e0bSBorislav Petkov (AMD) result = vc_handle_ioio(ghcb, ctxt);
197338918e0bSBorislav Petkov (AMD) break;
197438918e0bSBorislav Petkov (AMD) case SVM_EXIT_MSR:
197538918e0bSBorislav Petkov (AMD) result = vc_handle_msr(ghcb, ctxt);
197638918e0bSBorislav Petkov (AMD) break;
197738918e0bSBorislav Petkov (AMD) case SVM_EXIT_VMMCALL:
197838918e0bSBorislav Petkov (AMD) result = vc_handle_vmmcall(ghcb, ctxt);
197938918e0bSBorislav Petkov (AMD) break;
198038918e0bSBorislav Petkov (AMD) case SVM_EXIT_WBINVD:
198138918e0bSBorislav Petkov (AMD) result = vc_handle_wbinvd(ghcb, ctxt);
198238918e0bSBorislav Petkov (AMD) break;
198338918e0bSBorislav Petkov (AMD) case SVM_EXIT_MONITOR:
198438918e0bSBorislav Petkov (AMD) result = vc_handle_monitor(ghcb, ctxt);
198538918e0bSBorislav Petkov (AMD) break;
198638918e0bSBorislav Petkov (AMD) case SVM_EXIT_MWAIT:
198738918e0bSBorislav Petkov (AMD) result = vc_handle_mwait(ghcb, ctxt);
198838918e0bSBorislav Petkov (AMD) break;
198938918e0bSBorislav Petkov (AMD) case SVM_EXIT_NPF:
199038918e0bSBorislav Petkov (AMD) result = vc_handle_mmio(ghcb, ctxt);
199138918e0bSBorislav Petkov (AMD) break;
199238918e0bSBorislav Petkov (AMD) default:
199338918e0bSBorislav Petkov (AMD) /*
199438918e0bSBorislav Petkov (AMD) * Unexpected #VC exception
199538918e0bSBorislav Petkov (AMD) */
199638918e0bSBorislav Petkov (AMD) result = ES_UNSUPPORTED;
199738918e0bSBorislav Petkov (AMD) }
199838918e0bSBorislav Petkov (AMD)
199938918e0bSBorislav Petkov (AMD) return result;
200038918e0bSBorislav Petkov (AMD) }
200138918e0bSBorislav Petkov (AMD)
is_vc2_stack(unsigned long sp)200238918e0bSBorislav Petkov (AMD) static __always_inline bool is_vc2_stack(unsigned long sp)
200338918e0bSBorislav Petkov (AMD) {
200438918e0bSBorislav Petkov (AMD) return (sp >= __this_cpu_ist_bottom_va(VC2) && sp < __this_cpu_ist_top_va(VC2));
200538918e0bSBorislav Petkov (AMD) }
200638918e0bSBorislav Petkov (AMD)
vc_from_invalid_context(struct pt_regs * regs)200738918e0bSBorislav Petkov (AMD) static __always_inline bool vc_from_invalid_context(struct pt_regs *regs)
200838918e0bSBorislav Petkov (AMD) {
200938918e0bSBorislav Petkov (AMD) unsigned long sp, prev_sp;
201038918e0bSBorislav Petkov (AMD)
201138918e0bSBorislav Petkov (AMD) sp = (unsigned long)regs;
201238918e0bSBorislav Petkov (AMD) prev_sp = regs->sp;
201338918e0bSBorislav Petkov (AMD)
201438918e0bSBorislav Petkov (AMD) /*
201538918e0bSBorislav Petkov (AMD) * If the code was already executing on the VC2 stack when the #VC
201638918e0bSBorislav Petkov (AMD) * happened, let it proceed to the normal handling routine. This way the
201738918e0bSBorislav Petkov (AMD) * code executing on the VC2 stack can cause #VC exceptions to get handled.
201838918e0bSBorislav Petkov (AMD) */
201938918e0bSBorislav Petkov (AMD) return is_vc2_stack(sp) && !is_vc2_stack(prev_sp);
202038918e0bSBorislav Petkov (AMD) }
202138918e0bSBorislav Petkov (AMD)
vc_raw_handle_exception(struct pt_regs * regs,unsigned long error_code)202238918e0bSBorislav Petkov (AMD) static bool vc_raw_handle_exception(struct pt_regs *regs, unsigned long error_code)
202338918e0bSBorislav Petkov (AMD) {
202438918e0bSBorislav Petkov (AMD) struct ghcb_state state;
202538918e0bSBorislav Petkov (AMD) struct es_em_ctxt ctxt;
202638918e0bSBorislav Petkov (AMD) enum es_result result;
202738918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
202838918e0bSBorislav Petkov (AMD) bool ret = true;
202938918e0bSBorislav Petkov (AMD)
203038918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
203138918e0bSBorislav Petkov (AMD)
203238918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(ghcb);
203338918e0bSBorislav Petkov (AMD) result = vc_init_em_ctxt(&ctxt, regs, error_code);
203438918e0bSBorislav Petkov (AMD)
203538918e0bSBorislav Petkov (AMD) if (result == ES_OK)
203638918e0bSBorislav Petkov (AMD) result = vc_handle_exitcode(&ctxt, ghcb, error_code);
203738918e0bSBorislav Petkov (AMD)
203838918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
203938918e0bSBorislav Petkov (AMD)
204038918e0bSBorislav Petkov (AMD) /* Done - now check the result */
204138918e0bSBorislav Petkov (AMD) switch (result) {
204238918e0bSBorislav Petkov (AMD) case ES_OK:
204338918e0bSBorislav Petkov (AMD) vc_finish_insn(&ctxt);
204438918e0bSBorislav Petkov (AMD) break;
204538918e0bSBorislav Petkov (AMD) case ES_UNSUPPORTED:
204638918e0bSBorislav Petkov (AMD) pr_err_ratelimited("Unsupported exit-code 0x%02lx in #VC exception (IP: 0x%lx)\n",
204738918e0bSBorislav Petkov (AMD) error_code, regs->ip);
204838918e0bSBorislav Petkov (AMD) ret = false;
204938918e0bSBorislav Petkov (AMD) break;
205038918e0bSBorislav Petkov (AMD) case ES_VMM_ERROR:
205138918e0bSBorislav Petkov (AMD) pr_err_ratelimited("Failure in communication with VMM (exit-code 0x%02lx IP: 0x%lx)\n",
205238918e0bSBorislav Petkov (AMD) error_code, regs->ip);
205338918e0bSBorislav Petkov (AMD) ret = false;
205438918e0bSBorislav Petkov (AMD) break;
205538918e0bSBorislav Petkov (AMD) case ES_DECODE_FAILED:
205638918e0bSBorislav Petkov (AMD) pr_err_ratelimited("Failed to decode instruction (exit-code 0x%02lx IP: 0x%lx)\n",
205738918e0bSBorislav Petkov (AMD) error_code, regs->ip);
205838918e0bSBorislav Petkov (AMD) ret = false;
205938918e0bSBorislav Petkov (AMD) break;
206038918e0bSBorislav Petkov (AMD) case ES_EXCEPTION:
206138918e0bSBorislav Petkov (AMD) vc_forward_exception(&ctxt);
206238918e0bSBorislav Petkov (AMD) break;
206338918e0bSBorislav Petkov (AMD) case ES_RETRY:
206438918e0bSBorislav Petkov (AMD) /* Nothing to do */
206538918e0bSBorislav Petkov (AMD) break;
206638918e0bSBorislav Petkov (AMD) default:
206738918e0bSBorislav Petkov (AMD) pr_emerg("Unknown result in %s():%d\n", __func__, result);
206838918e0bSBorislav Petkov (AMD) /*
206938918e0bSBorislav Petkov (AMD) * Emulating the instruction which caused the #VC exception
207038918e0bSBorislav Petkov (AMD) * failed - can't continue so print debug information
207138918e0bSBorislav Petkov (AMD) */
207238918e0bSBorislav Petkov (AMD) BUG();
207338918e0bSBorislav Petkov (AMD) }
207438918e0bSBorislav Petkov (AMD)
207538918e0bSBorislav Petkov (AMD) return ret;
207638918e0bSBorislav Petkov (AMD) }
207738918e0bSBorislav Petkov (AMD)
vc_is_db(unsigned long error_code)207838918e0bSBorislav Petkov (AMD) static __always_inline bool vc_is_db(unsigned long error_code)
207938918e0bSBorislav Petkov (AMD) {
208038918e0bSBorislav Petkov (AMD) return error_code == SVM_EXIT_EXCP_BASE + X86_TRAP_DB;
208138918e0bSBorislav Petkov (AMD) }
208238918e0bSBorislav Petkov (AMD)
208338918e0bSBorislav Petkov (AMD) /*
208438918e0bSBorislav Petkov (AMD) * Runtime #VC exception handler when raised from kernel mode. Runs in NMI mode
208538918e0bSBorislav Petkov (AMD) * and will panic when an error happens.
208638918e0bSBorislav Petkov (AMD) */
DEFINE_IDTENTRY_VC_KERNEL(exc_vmm_communication)208738918e0bSBorislav Petkov (AMD) DEFINE_IDTENTRY_VC_KERNEL(exc_vmm_communication)
208838918e0bSBorislav Petkov (AMD) {
208938918e0bSBorislav Petkov (AMD) irqentry_state_t irq_state;
209038918e0bSBorislav Petkov (AMD)
209138918e0bSBorislav Petkov (AMD) /*
209238918e0bSBorislav Petkov (AMD) * With the current implementation it is always possible to switch to a
209338918e0bSBorislav Petkov (AMD) * safe stack because #VC exceptions only happen at known places, like
209438918e0bSBorislav Petkov (AMD) * intercepted instructions or accesses to MMIO areas/IO ports. They can
209538918e0bSBorislav Petkov (AMD) * also happen with code instrumentation when the hypervisor intercepts
209638918e0bSBorislav Petkov (AMD) * #DB, but the critical paths are forbidden to be instrumented, so #DB
209738918e0bSBorislav Petkov (AMD) * exceptions currently also only happen in safe places.
209838918e0bSBorislav Petkov (AMD) *
209938918e0bSBorislav Petkov (AMD) * But keep this here in case the noinstr annotations are violated due
210038918e0bSBorislav Petkov (AMD) * to bug elsewhere.
210138918e0bSBorislav Petkov (AMD) */
210238918e0bSBorislav Petkov (AMD) if (unlikely(vc_from_invalid_context(regs))) {
210338918e0bSBorislav Petkov (AMD) instrumentation_begin();
210438918e0bSBorislav Petkov (AMD) panic("Can't handle #VC exception from unsupported context\n");
210538918e0bSBorislav Petkov (AMD) instrumentation_end();
210638918e0bSBorislav Petkov (AMD) }
210738918e0bSBorislav Petkov (AMD)
210838918e0bSBorislav Petkov (AMD) /*
210938918e0bSBorislav Petkov (AMD) * Handle #DB before calling into !noinstr code to avoid recursive #DB.
211038918e0bSBorislav Petkov (AMD) */
211138918e0bSBorislav Petkov (AMD) if (vc_is_db(error_code)) {
211238918e0bSBorislav Petkov (AMD) exc_debug(regs);
211338918e0bSBorislav Petkov (AMD) return;
211438918e0bSBorislav Petkov (AMD) }
211538918e0bSBorislav Petkov (AMD)
211638918e0bSBorislav Petkov (AMD) irq_state = irqentry_nmi_enter(regs);
211738918e0bSBorislav Petkov (AMD)
211838918e0bSBorislav Petkov (AMD) instrumentation_begin();
211938918e0bSBorislav Petkov (AMD)
212038918e0bSBorislav Petkov (AMD) if (!vc_raw_handle_exception(regs, error_code)) {
212138918e0bSBorislav Petkov (AMD) /* Show some debug info */
212238918e0bSBorislav Petkov (AMD) show_regs(regs);
212338918e0bSBorislav Petkov (AMD)
212438918e0bSBorislav Petkov (AMD) /* Ask hypervisor to sev_es_terminate */
212538918e0bSBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
212638918e0bSBorislav Petkov (AMD)
212738918e0bSBorislav Petkov (AMD) /* If that fails and we get here - just panic */
212838918e0bSBorislav Petkov (AMD) panic("Returned from Terminate-Request to Hypervisor\n");
212938918e0bSBorislav Petkov (AMD) }
213038918e0bSBorislav Petkov (AMD)
213138918e0bSBorislav Petkov (AMD) instrumentation_end();
213238918e0bSBorislav Petkov (AMD) irqentry_nmi_exit(regs, irq_state);
213338918e0bSBorislav Petkov (AMD) }
213438918e0bSBorislav Petkov (AMD)
213538918e0bSBorislav Petkov (AMD) /*
213638918e0bSBorislav Petkov (AMD) * Runtime #VC exception handler when raised from user mode. Runs in IRQ mode
213738918e0bSBorislav Petkov (AMD) * and will kill the current task with SIGBUS when an error happens.
213838918e0bSBorislav Petkov (AMD) */
DEFINE_IDTENTRY_VC_USER(exc_vmm_communication)213938918e0bSBorislav Petkov (AMD) DEFINE_IDTENTRY_VC_USER(exc_vmm_communication)
214038918e0bSBorislav Petkov (AMD) {
214138918e0bSBorislav Petkov (AMD) /*
214238918e0bSBorislav Petkov (AMD) * Handle #DB before calling into !noinstr code to avoid recursive #DB.
214338918e0bSBorislav Petkov (AMD) */
214438918e0bSBorislav Petkov (AMD) if (vc_is_db(error_code)) {
214538918e0bSBorislav Petkov (AMD) noist_exc_debug(regs);
214638918e0bSBorislav Petkov (AMD) return;
214738918e0bSBorislav Petkov (AMD) }
214838918e0bSBorislav Petkov (AMD)
214938918e0bSBorislav Petkov (AMD) irqentry_enter_from_user_mode(regs);
215038918e0bSBorislav Petkov (AMD) instrumentation_begin();
215138918e0bSBorislav Petkov (AMD)
215238918e0bSBorislav Petkov (AMD) if (!vc_raw_handle_exception(regs, error_code)) {
215338918e0bSBorislav Petkov (AMD) /*
215438918e0bSBorislav Petkov (AMD) * Do not kill the machine if user-space triggered the
215538918e0bSBorislav Petkov (AMD) * exception. Send SIGBUS instead and let user-space deal with
215638918e0bSBorislav Petkov (AMD) * it.
215738918e0bSBorislav Petkov (AMD) */
215838918e0bSBorislav Petkov (AMD) force_sig_fault(SIGBUS, BUS_OBJERR, (void __user *)0);
215938918e0bSBorislav Petkov (AMD) }
216038918e0bSBorislav Petkov (AMD)
216138918e0bSBorislav Petkov (AMD) instrumentation_end();
216238918e0bSBorislav Petkov (AMD) irqentry_exit_to_user_mode(regs);
216338918e0bSBorislav Petkov (AMD) }
216438918e0bSBorislav Petkov (AMD)
handle_vc_boot_ghcb(struct pt_regs * regs)216538918e0bSBorislav Petkov (AMD) bool __init handle_vc_boot_ghcb(struct pt_regs *regs)
216638918e0bSBorislav Petkov (AMD) {
216738918e0bSBorislav Petkov (AMD) unsigned long exit_code = regs->orig_ax;
216838918e0bSBorislav Petkov (AMD) struct es_em_ctxt ctxt;
216938918e0bSBorislav Petkov (AMD) enum es_result result;
217038918e0bSBorislav Petkov (AMD)
217138918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(boot_ghcb);
217238918e0bSBorislav Petkov (AMD)
217338918e0bSBorislav Petkov (AMD) result = vc_init_em_ctxt(&ctxt, regs, exit_code);
217438918e0bSBorislav Petkov (AMD) if (result == ES_OK)
217538918e0bSBorislav Petkov (AMD) result = vc_handle_exitcode(&ctxt, boot_ghcb, exit_code);
217638918e0bSBorislav Petkov (AMD)
217738918e0bSBorislav Petkov (AMD) /* Done - now check the result */
217838918e0bSBorislav Petkov (AMD) switch (result) {
217938918e0bSBorislav Petkov (AMD) case ES_OK:
218038918e0bSBorislav Petkov (AMD) vc_finish_insn(&ctxt);
218138918e0bSBorislav Petkov (AMD) break;
218238918e0bSBorislav Petkov (AMD) case ES_UNSUPPORTED:
218338918e0bSBorislav Petkov (AMD) early_printk("PANIC: Unsupported exit-code 0x%02lx in early #VC exception (IP: 0x%lx)\n",
218438918e0bSBorislav Petkov (AMD) exit_code, regs->ip);
218538918e0bSBorislav Petkov (AMD) goto fail;
218638918e0bSBorislav Petkov (AMD) case ES_VMM_ERROR:
218738918e0bSBorislav Petkov (AMD) early_printk("PANIC: Failure in communication with VMM (exit-code 0x%02lx IP: 0x%lx)\n",
218838918e0bSBorislav Petkov (AMD) exit_code, regs->ip);
218938918e0bSBorislav Petkov (AMD) goto fail;
219038918e0bSBorislav Petkov (AMD) case ES_DECODE_FAILED:
219138918e0bSBorislav Petkov (AMD) early_printk("PANIC: Failed to decode instruction (exit-code 0x%02lx IP: 0x%lx)\n",
219238918e0bSBorislav Petkov (AMD) exit_code, regs->ip);
219338918e0bSBorislav Petkov (AMD) goto fail;
219438918e0bSBorislav Petkov (AMD) case ES_EXCEPTION:
219538918e0bSBorislav Petkov (AMD) vc_early_forward_exception(&ctxt);
219638918e0bSBorislav Petkov (AMD) break;
219738918e0bSBorislav Petkov (AMD) case ES_RETRY:
219838918e0bSBorislav Petkov (AMD) /* Nothing to do */
219938918e0bSBorislav Petkov (AMD) break;
220038918e0bSBorislav Petkov (AMD) default:
220138918e0bSBorislav Petkov (AMD) BUG();
220238918e0bSBorislav Petkov (AMD) }
220338918e0bSBorislav Petkov (AMD)
220438918e0bSBorislav Petkov (AMD) return true;
220538918e0bSBorislav Petkov (AMD)
220638918e0bSBorislav Petkov (AMD) fail:
220738918e0bSBorislav Petkov (AMD) show_regs(regs);
220838918e0bSBorislav Petkov (AMD)
220938918e0bSBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
221038918e0bSBorislav Petkov (AMD) }
221138918e0bSBorislav Petkov (AMD)
221238918e0bSBorislav Petkov (AMD) /*
221338918e0bSBorislav Petkov (AMD) * Initial set up of SNP relies on information provided by the
221438918e0bSBorislav Petkov (AMD) * Confidential Computing blob, which can be passed to the kernel
221538918e0bSBorislav Petkov (AMD) * in the following ways, depending on how it is booted:
221638918e0bSBorislav Petkov (AMD) *
221738918e0bSBorislav Petkov (AMD) * - when booted via the boot/decompress kernel:
221838918e0bSBorislav Petkov (AMD) * - via boot_params
221938918e0bSBorislav Petkov (AMD) *
222038918e0bSBorislav Petkov (AMD) * - when booted directly by firmware/bootloader (e.g. CONFIG_PVH):
222138918e0bSBorislav Petkov (AMD) * - via a setup_data entry, as defined by the Linux Boot Protocol
222238918e0bSBorislav Petkov (AMD) *
222338918e0bSBorislav Petkov (AMD) * Scan for the blob in that order.
222438918e0bSBorislav Petkov (AMD) */
find_cc_blob(struct boot_params * bp)222538918e0bSBorislav Petkov (AMD) static __head struct cc_blob_sev_info *find_cc_blob(struct boot_params *bp)
222638918e0bSBorislav Petkov (AMD) {
222738918e0bSBorislav Petkov (AMD) struct cc_blob_sev_info *cc_info;
222838918e0bSBorislav Petkov (AMD)
222938918e0bSBorislav Petkov (AMD) /* Boot kernel would have passed the CC blob via boot_params. */
223038918e0bSBorislav Petkov (AMD) if (bp->cc_blob_address) {
223138918e0bSBorislav Petkov (AMD) cc_info = (struct cc_blob_sev_info *)(unsigned long)bp->cc_blob_address;
223238918e0bSBorislav Petkov (AMD) goto found_cc_info;
223338918e0bSBorislav Petkov (AMD) }
223438918e0bSBorislav Petkov (AMD)
223538918e0bSBorislav Petkov (AMD) /*
223638918e0bSBorislav Petkov (AMD) * If kernel was booted directly, without the use of the
223738918e0bSBorislav Petkov (AMD) * boot/decompression kernel, the CC blob may have been passed via
223838918e0bSBorislav Petkov (AMD) * setup_data instead.
223938918e0bSBorislav Petkov (AMD) */
224038918e0bSBorislav Petkov (AMD) cc_info = find_cc_blob_setup_data(bp);
224138918e0bSBorislav Petkov (AMD) if (!cc_info)
224238918e0bSBorislav Petkov (AMD) return NULL;
224338918e0bSBorislav Petkov (AMD)
224438918e0bSBorislav Petkov (AMD) found_cc_info:
224538918e0bSBorislav Petkov (AMD) if (cc_info->magic != CC_BLOB_SEV_HDR_MAGIC)
224638918e0bSBorislav Petkov (AMD) snp_abort();
224738918e0bSBorislav Petkov (AMD)
224838918e0bSBorislav Petkov (AMD) return cc_info;
224938918e0bSBorislav Petkov (AMD) }
225038918e0bSBorislav Petkov (AMD)
svsm_setup(struct cc_blob_sev_info * cc_info)225138918e0bSBorislav Petkov (AMD) static __head void svsm_setup(struct cc_blob_sev_info *cc_info)
225238918e0bSBorislav Petkov (AMD) {
225338918e0bSBorislav Petkov (AMD) struct svsm_call call = {};
225438918e0bSBorislav Petkov (AMD) int ret;
225538918e0bSBorislav Petkov (AMD) u64 pa;
225638918e0bSBorislav Petkov (AMD)
225738918e0bSBorislav Petkov (AMD) /*
225838918e0bSBorislav Petkov (AMD) * Record the SVSM Calling Area address (CAA) if the guest is not
225938918e0bSBorislav Petkov (AMD) * running at VMPL0. The CA will be used to communicate with the
226038918e0bSBorislav Petkov (AMD) * SVSM to perform the SVSM services.
226138918e0bSBorislav Petkov (AMD) */
226238918e0bSBorislav Petkov (AMD) if (!svsm_setup_ca(cc_info))
226338918e0bSBorislav Petkov (AMD) return;
226438918e0bSBorislav Petkov (AMD)
226538918e0bSBorislav Petkov (AMD) /*
226638918e0bSBorislav Petkov (AMD) * It is very early in the boot and the kernel is running identity
226738918e0bSBorislav Petkov (AMD) * mapped but without having adjusted the pagetables to where the
226838918e0bSBorislav Petkov (AMD) * kernel was loaded (physbase), so the get the CA address using
226938918e0bSBorislav Petkov (AMD) * RIP-relative addressing.
227038918e0bSBorislav Petkov (AMD) */
227138918e0bSBorislav Petkov (AMD) pa = (u64)&RIP_REL_REF(boot_svsm_ca_page);
227238918e0bSBorislav Petkov (AMD)
227338918e0bSBorislav Petkov (AMD) /*
227438918e0bSBorislav Petkov (AMD) * Switch over to the boot SVSM CA while the current CA is still
227538918e0bSBorislav Petkov (AMD) * addressable. There is no GHCB at this point so use the MSR protocol.
227638918e0bSBorislav Petkov (AMD) *
227738918e0bSBorislav Petkov (AMD) * SVSM_CORE_REMAP_CA call:
227838918e0bSBorislav Petkov (AMD) * RAX = 0 (Protocol=0, CallID=0)
227938918e0bSBorislav Petkov (AMD) * RCX = New CA GPA
228038918e0bSBorislav Petkov (AMD) */
228138918e0bSBorislav Petkov (AMD) call.caa = svsm_get_caa();
228238918e0bSBorislav Petkov (AMD) call.rax = SVSM_CORE_CALL(SVSM_CORE_REMAP_CA);
228338918e0bSBorislav Petkov (AMD) call.rcx = pa;
228438918e0bSBorislav Petkov (AMD) ret = svsm_perform_call_protocol(&call);
228538918e0bSBorislav Petkov (AMD) if (ret)
228638918e0bSBorislav Petkov (AMD) panic("Can't remap the SVSM CA, ret=%d, rax_out=0x%llx\n", ret, call.rax_out);
228738918e0bSBorislav Petkov (AMD)
228838918e0bSBorislav Petkov (AMD) RIP_REL_REF(boot_svsm_caa) = (struct svsm_ca *)pa;
228938918e0bSBorislav Petkov (AMD) RIP_REL_REF(boot_svsm_caa_pa) = pa;
229038918e0bSBorislav Petkov (AMD) }
229138918e0bSBorislav Petkov (AMD)
snp_init(struct boot_params * bp)229238918e0bSBorislav Petkov (AMD) bool __head snp_init(struct boot_params *bp)
229338918e0bSBorislav Petkov (AMD) {
229438918e0bSBorislav Petkov (AMD) struct cc_blob_sev_info *cc_info;
229538918e0bSBorislav Petkov (AMD)
229638918e0bSBorislav Petkov (AMD) if (!bp)
229738918e0bSBorislav Petkov (AMD) return false;
229838918e0bSBorislav Petkov (AMD)
229938918e0bSBorislav Petkov (AMD) cc_info = find_cc_blob(bp);
230038918e0bSBorislav Petkov (AMD) if (!cc_info)
230138918e0bSBorislav Petkov (AMD) return false;
230238918e0bSBorislav Petkov (AMD)
230338918e0bSBorislav Petkov (AMD) setup_cpuid_table(cc_info);
230438918e0bSBorislav Petkov (AMD)
230538918e0bSBorislav Petkov (AMD) svsm_setup(cc_info);
230638918e0bSBorislav Petkov (AMD)
230738918e0bSBorislav Petkov (AMD) /*
230838918e0bSBorislav Petkov (AMD) * The CC blob will be used later to access the secrets page. Cache
230938918e0bSBorislav Petkov (AMD) * it here like the boot kernel does.
231038918e0bSBorislav Petkov (AMD) */
231138918e0bSBorislav Petkov (AMD) bp->cc_blob_address = (u32)(unsigned long)cc_info;
231238918e0bSBorislav Petkov (AMD)
231338918e0bSBorislav Petkov (AMD) return true;
231438918e0bSBorislav Petkov (AMD) }
231538918e0bSBorislav Petkov (AMD)
snp_abort(void)231638918e0bSBorislav Petkov (AMD) void __head __noreturn snp_abort(void)
231738918e0bSBorislav Petkov (AMD) {
231838918e0bSBorislav Petkov (AMD) sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
231938918e0bSBorislav Petkov (AMD) }
232038918e0bSBorislav Petkov (AMD)
232138918e0bSBorislav Petkov (AMD) /*
232238918e0bSBorislav Petkov (AMD) * SEV-SNP guests should only execute dmi_setup() if EFI_CONFIG_TABLES are
232338918e0bSBorislav Petkov (AMD) * enabled, as the alternative (fallback) logic for DMI probing in the legacy
232438918e0bSBorislav Petkov (AMD) * ROM region can cause a crash since this region is not pre-validated.
232538918e0bSBorislav Petkov (AMD) */
snp_dmi_setup(void)232638918e0bSBorislav Petkov (AMD) void __init snp_dmi_setup(void)
232738918e0bSBorislav Petkov (AMD) {
232838918e0bSBorislav Petkov (AMD) if (efi_enabled(EFI_CONFIG_TABLES))
232938918e0bSBorislav Petkov (AMD) dmi_setup();
233038918e0bSBorislav Petkov (AMD) }
233138918e0bSBorislav Petkov (AMD)
dump_cpuid_table(void)233238918e0bSBorislav Petkov (AMD) static void dump_cpuid_table(void)
233338918e0bSBorislav Petkov (AMD) {
233438918e0bSBorislav Petkov (AMD) const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table();
233538918e0bSBorislav Petkov (AMD) int i = 0;
233638918e0bSBorislav Petkov (AMD)
233738918e0bSBorislav Petkov (AMD) pr_info("count=%d reserved=0x%x reserved2=0x%llx\n",
233838918e0bSBorislav Petkov (AMD) cpuid_table->count, cpuid_table->__reserved1, cpuid_table->__reserved2);
233938918e0bSBorislav Petkov (AMD)
234038918e0bSBorislav Petkov (AMD) for (i = 0; i < SNP_CPUID_COUNT_MAX; i++) {
234138918e0bSBorislav Petkov (AMD) const struct snp_cpuid_fn *fn = &cpuid_table->fn[i];
234238918e0bSBorislav Petkov (AMD)
234338918e0bSBorislav Petkov (AMD) pr_info("index=%3d fn=0x%08x subfn=0x%08x: eax=0x%08x ebx=0x%08x ecx=0x%08x edx=0x%08x xcr0_in=0x%016llx xss_in=0x%016llx reserved=0x%016llx\n",
234438918e0bSBorislav Petkov (AMD) i, fn->eax_in, fn->ecx_in, fn->eax, fn->ebx, fn->ecx,
234538918e0bSBorislav Petkov (AMD) fn->edx, fn->xcr0_in, fn->xss_in, fn->__reserved);
234638918e0bSBorislav Petkov (AMD) }
234738918e0bSBorislav Petkov (AMD) }
234838918e0bSBorislav Petkov (AMD)
234938918e0bSBorislav Petkov (AMD) /*
235038918e0bSBorislav Petkov (AMD) * It is useful from an auditing/testing perspective to provide an easy way
235138918e0bSBorislav Petkov (AMD) * for the guest owner to know that the CPUID table has been initialized as
235238918e0bSBorislav Petkov (AMD) * expected, but that initialization happens too early in boot to print any
235338918e0bSBorislav Petkov (AMD) * sort of indicator, and there's not really any other good place to do it,
235438918e0bSBorislav Petkov (AMD) * so do it here.
235538918e0bSBorislav Petkov (AMD) *
235638918e0bSBorislav Petkov (AMD) * If running as an SNP guest, report the current VM privilege level (VMPL).
235738918e0bSBorislav Petkov (AMD) */
report_snp_info(void)235838918e0bSBorislav Petkov (AMD) static int __init report_snp_info(void)
235938918e0bSBorislav Petkov (AMD) {
236038918e0bSBorislav Petkov (AMD) const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table();
236138918e0bSBorislav Petkov (AMD)
236238918e0bSBorislav Petkov (AMD) if (cpuid_table->count) {
236338918e0bSBorislav Petkov (AMD) pr_info("Using SNP CPUID table, %d entries present.\n",
236438918e0bSBorislav Petkov (AMD) cpuid_table->count);
236538918e0bSBorislav Petkov (AMD)
236638918e0bSBorislav Petkov (AMD) if (sev_cfg.debug)
236738918e0bSBorislav Petkov (AMD) dump_cpuid_table();
236838918e0bSBorislav Petkov (AMD) }
236938918e0bSBorislav Petkov (AMD)
237038918e0bSBorislav Petkov (AMD) if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
237138918e0bSBorislav Petkov (AMD) pr_info("SNP running at VMPL%u.\n", snp_vmpl);
237238918e0bSBorislav Petkov (AMD)
237338918e0bSBorislav Petkov (AMD) return 0;
237438918e0bSBorislav Petkov (AMD) }
237538918e0bSBorislav Petkov (AMD) arch_initcall(report_snp_info);
237638918e0bSBorislav Petkov (AMD)
init_sev_config(char * str)237738918e0bSBorislav Petkov (AMD) static int __init init_sev_config(char *str)
237838918e0bSBorislav Petkov (AMD) {
237938918e0bSBorislav Petkov (AMD) char *s;
238038918e0bSBorislav Petkov (AMD)
238138918e0bSBorislav Petkov (AMD) while ((s = strsep(&str, ","))) {
238238918e0bSBorislav Petkov (AMD) if (!strcmp(s, "debug")) {
238338918e0bSBorislav Petkov (AMD) sev_cfg.debug = true;
238438918e0bSBorislav Petkov (AMD) continue;
238538918e0bSBorislav Petkov (AMD) }
238638918e0bSBorislav Petkov (AMD)
238738918e0bSBorislav Petkov (AMD) pr_info("SEV command-line option '%s' was not recognized\n", s);
238838918e0bSBorislav Petkov (AMD) }
238938918e0bSBorislav Petkov (AMD)
239038918e0bSBorislav Petkov (AMD) return 1;
239138918e0bSBorislav Petkov (AMD) }
239238918e0bSBorislav Petkov (AMD) __setup("sev=", init_sev_config);
239338918e0bSBorislav Petkov (AMD)
update_attest_input(struct svsm_call * call,struct svsm_attest_call * input)239438918e0bSBorislav Petkov (AMD) static void update_attest_input(struct svsm_call *call, struct svsm_attest_call *input)
239538918e0bSBorislav Petkov (AMD) {
239638918e0bSBorislav Petkov (AMD) /* If (new) lengths have been returned, propagate them up */
239738918e0bSBorislav Petkov (AMD) if (call->rcx_out != call->rcx)
239838918e0bSBorislav Petkov (AMD) input->manifest_buf.len = call->rcx_out;
239938918e0bSBorislav Petkov (AMD)
240038918e0bSBorislav Petkov (AMD) if (call->rdx_out != call->rdx)
240138918e0bSBorislav Petkov (AMD) input->certificates_buf.len = call->rdx_out;
240238918e0bSBorislav Petkov (AMD)
240338918e0bSBorislav Petkov (AMD) if (call->r8_out != call->r8)
240438918e0bSBorislav Petkov (AMD) input->report_buf.len = call->r8_out;
240538918e0bSBorislav Petkov (AMD) }
240638918e0bSBorislav Petkov (AMD)
snp_issue_svsm_attest_req(u64 call_id,struct svsm_call * call,struct svsm_attest_call * input)240738918e0bSBorislav Petkov (AMD) int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call,
240838918e0bSBorislav Petkov (AMD) struct svsm_attest_call *input)
240938918e0bSBorislav Petkov (AMD) {
241038918e0bSBorislav Petkov (AMD) struct svsm_attest_call *ac;
241138918e0bSBorislav Petkov (AMD) unsigned long flags;
241238918e0bSBorislav Petkov (AMD) u64 attest_call_pa;
241338918e0bSBorislav Petkov (AMD) int ret;
241438918e0bSBorislav Petkov (AMD)
241538918e0bSBorislav Petkov (AMD) if (!snp_vmpl)
241638918e0bSBorislav Petkov (AMD) return -EINVAL;
241738918e0bSBorislav Petkov (AMD)
241838918e0bSBorislav Petkov (AMD) local_irq_save(flags);
241938918e0bSBorislav Petkov (AMD)
242038918e0bSBorislav Petkov (AMD) call->caa = svsm_get_caa();
242138918e0bSBorislav Petkov (AMD)
242238918e0bSBorislav Petkov (AMD) ac = (struct svsm_attest_call *)call->caa->svsm_buffer;
242338918e0bSBorislav Petkov (AMD) attest_call_pa = svsm_get_caa_pa() + offsetof(struct svsm_ca, svsm_buffer);
242438918e0bSBorislav Petkov (AMD)
242538918e0bSBorislav Petkov (AMD) *ac = *input;
242638918e0bSBorislav Petkov (AMD)
242738918e0bSBorislav Petkov (AMD) /*
242838918e0bSBorislav Petkov (AMD) * Set input registers for the request and set RDX and R8 to known
242938918e0bSBorislav Petkov (AMD) * values in order to detect length values being returned in them.
243038918e0bSBorislav Petkov (AMD) */
243138918e0bSBorislav Petkov (AMD) call->rax = call_id;
243238918e0bSBorislav Petkov (AMD) call->rcx = attest_call_pa;
243338918e0bSBorislav Petkov (AMD) call->rdx = -1;
243438918e0bSBorislav Petkov (AMD) call->r8 = -1;
243538918e0bSBorislav Petkov (AMD) ret = svsm_perform_call_protocol(call);
243638918e0bSBorislav Petkov (AMD) update_attest_input(call, input);
243738918e0bSBorislav Petkov (AMD)
243838918e0bSBorislav Petkov (AMD) local_irq_restore(flags);
243938918e0bSBorislav Petkov (AMD)
244038918e0bSBorislav Petkov (AMD) return ret;
244138918e0bSBorislav Petkov (AMD) }
244238918e0bSBorislav Petkov (AMD) EXPORT_SYMBOL_GPL(snp_issue_svsm_attest_req);
244338918e0bSBorislav Petkov (AMD)
snp_issue_guest_request(u64 exit_code,struct snp_req_data * input,struct snp_guest_request_ioctl * rio)244438918e0bSBorislav Petkov (AMD) int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
244538918e0bSBorislav Petkov (AMD) {
244638918e0bSBorislav Petkov (AMD) struct ghcb_state state;
244738918e0bSBorislav Petkov (AMD) struct es_em_ctxt ctxt;
244838918e0bSBorislav Petkov (AMD) unsigned long flags;
244938918e0bSBorislav Petkov (AMD) struct ghcb *ghcb;
245038918e0bSBorislav Petkov (AMD) int ret;
245138918e0bSBorislav Petkov (AMD)
245238918e0bSBorislav Petkov (AMD) rio->exitinfo2 = SEV_RET_NO_FW_CALL;
245338918e0bSBorislav Petkov (AMD)
245438918e0bSBorislav Petkov (AMD) /*
245538918e0bSBorislav Petkov (AMD) * __sev_get_ghcb() needs to run with IRQs disabled because it is using
245638918e0bSBorislav Petkov (AMD) * a per-CPU GHCB.
245738918e0bSBorislav Petkov (AMD) */
245838918e0bSBorislav Petkov (AMD) local_irq_save(flags);
245938918e0bSBorislav Petkov (AMD)
246038918e0bSBorislav Petkov (AMD) ghcb = __sev_get_ghcb(&state);
246138918e0bSBorislav Petkov (AMD) if (!ghcb) {
246238918e0bSBorislav Petkov (AMD) ret = -EIO;
246338918e0bSBorislav Petkov (AMD) goto e_restore_irq;
246438918e0bSBorislav Petkov (AMD) }
246538918e0bSBorislav Petkov (AMD)
246638918e0bSBorislav Petkov (AMD) vc_ghcb_invalidate(ghcb);
246738918e0bSBorislav Petkov (AMD)
246838918e0bSBorislav Petkov (AMD) if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
246938918e0bSBorislav Petkov (AMD) ghcb_set_rax(ghcb, input->data_gpa);
247038918e0bSBorislav Petkov (AMD) ghcb_set_rbx(ghcb, input->data_npages);
247138918e0bSBorislav Petkov (AMD) }
247238918e0bSBorislav Petkov (AMD)
247338918e0bSBorislav Petkov (AMD) ret = sev_es_ghcb_hv_call(ghcb, &ctxt, exit_code, input->req_gpa, input->resp_gpa);
247438918e0bSBorislav Petkov (AMD) if (ret)
247538918e0bSBorislav Petkov (AMD) goto e_put;
247638918e0bSBorislav Petkov (AMD)
247738918e0bSBorislav Petkov (AMD) rio->exitinfo2 = ghcb->save.sw_exit_info_2;
247838918e0bSBorislav Petkov (AMD) switch (rio->exitinfo2) {
247938918e0bSBorislav Petkov (AMD) case 0:
248038918e0bSBorislav Petkov (AMD) break;
248138918e0bSBorislav Petkov (AMD)
248238918e0bSBorislav Petkov (AMD) case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_BUSY):
248338918e0bSBorislav Petkov (AMD) ret = -EAGAIN;
248438918e0bSBorislav Petkov (AMD) break;
248538918e0bSBorislav Petkov (AMD)
248638918e0bSBorislav Petkov (AMD) case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
248738918e0bSBorislav Petkov (AMD) /* Number of expected pages are returned in RBX */
248838918e0bSBorislav Petkov (AMD) if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
248938918e0bSBorislav Petkov (AMD) input->data_npages = ghcb_get_rbx(ghcb);
249038918e0bSBorislav Petkov (AMD) ret = -ENOSPC;
249138918e0bSBorislav Petkov (AMD) break;
249238918e0bSBorislav Petkov (AMD) }
249338918e0bSBorislav Petkov (AMD) fallthrough;
249438918e0bSBorislav Petkov (AMD) default:
249538918e0bSBorislav Petkov (AMD) ret = -EIO;
249638918e0bSBorislav Petkov (AMD) break;
249738918e0bSBorislav Petkov (AMD) }
249838918e0bSBorislav Petkov (AMD)
249938918e0bSBorislav Petkov (AMD) e_put:
250038918e0bSBorislav Petkov (AMD) __sev_put_ghcb(&state);
250138918e0bSBorislav Petkov (AMD) e_restore_irq:
250238918e0bSBorislav Petkov (AMD) local_irq_restore(flags);
250338918e0bSBorislav Petkov (AMD)
250438918e0bSBorislav Petkov (AMD) return ret;
250538918e0bSBorislav Petkov (AMD) }
250638918e0bSBorislav Petkov (AMD) EXPORT_SYMBOL_GPL(snp_issue_guest_request);
250738918e0bSBorislav Petkov (AMD)
250838918e0bSBorislav Petkov (AMD) static struct platform_device sev_guest_device = {
250938918e0bSBorislav Petkov (AMD) .name = "sev-guest",
251038918e0bSBorislav Petkov (AMD) .id = -1,
251138918e0bSBorislav Petkov (AMD) };
251238918e0bSBorislav Petkov (AMD)
snp_init_platform_device(void)251338918e0bSBorislav Petkov (AMD) static int __init snp_init_platform_device(void)
251438918e0bSBorislav Petkov (AMD) {
251538918e0bSBorislav Petkov (AMD) struct sev_guest_platform_data data;
251638918e0bSBorislav Petkov (AMD) u64 gpa;
251738918e0bSBorislav Petkov (AMD)
251838918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
251938918e0bSBorislav Petkov (AMD) return -ENODEV;
252038918e0bSBorislav Petkov (AMD)
252138918e0bSBorislav Petkov (AMD) gpa = get_secrets_page();
252238918e0bSBorislav Petkov (AMD) if (!gpa)
252338918e0bSBorislav Petkov (AMD) return -ENODEV;
252438918e0bSBorislav Petkov (AMD)
252538918e0bSBorislav Petkov (AMD) data.secrets_gpa = gpa;
252638918e0bSBorislav Petkov (AMD) if (platform_device_add_data(&sev_guest_device, &data, sizeof(data)))
252738918e0bSBorislav Petkov (AMD) return -ENODEV;
252838918e0bSBorislav Petkov (AMD)
252938918e0bSBorislav Petkov (AMD) if (platform_device_register(&sev_guest_device))
253038918e0bSBorislav Petkov (AMD) return -ENODEV;
253138918e0bSBorislav Petkov (AMD)
253238918e0bSBorislav Petkov (AMD) pr_info("SNP guest platform device initialized.\n");
253338918e0bSBorislav Petkov (AMD) return 0;
253438918e0bSBorislav Petkov (AMD) }
253538918e0bSBorislav Petkov (AMD) device_initcall(snp_init_platform_device);
253638918e0bSBorislav Petkov (AMD)
sev_show_status(void)253738918e0bSBorislav Petkov (AMD) void sev_show_status(void)
253838918e0bSBorislav Petkov (AMD) {
253938918e0bSBorislav Petkov (AMD) int i;
254038918e0bSBorislav Petkov (AMD)
254138918e0bSBorislav Petkov (AMD) pr_info("Status: ");
254238918e0bSBorislav Petkov (AMD) for (i = 0; i < MSR_AMD64_SNP_RESV_BIT; i++) {
254338918e0bSBorislav Petkov (AMD) if (sev_status & BIT_ULL(i)) {
254438918e0bSBorislav Petkov (AMD) if (!sev_status_feat_names[i])
254538918e0bSBorislav Petkov (AMD) continue;
254638918e0bSBorislav Petkov (AMD)
254738918e0bSBorislav Petkov (AMD) pr_cont("%s ", sev_status_feat_names[i]);
254838918e0bSBorislav Petkov (AMD) }
254938918e0bSBorislav Petkov (AMD) }
255038918e0bSBorislav Petkov (AMD) pr_cont("\n");
255138918e0bSBorislav Petkov (AMD) }
255238918e0bSBorislav Petkov (AMD)
snp_update_svsm_ca(void)255338918e0bSBorislav Petkov (AMD) void __init snp_update_svsm_ca(void)
255438918e0bSBorislav Petkov (AMD) {
255538918e0bSBorislav Petkov (AMD) if (!snp_vmpl)
255638918e0bSBorislav Petkov (AMD) return;
255738918e0bSBorislav Petkov (AMD)
255838918e0bSBorislav Petkov (AMD) /* Update the CAA to a proper kernel address */
255938918e0bSBorislav Petkov (AMD) boot_svsm_caa = &boot_svsm_ca_page;
256038918e0bSBorislav Petkov (AMD) }
256138918e0bSBorislav Petkov (AMD)
256238918e0bSBorislav Petkov (AMD) #ifdef CONFIG_SYSFS
vmpl_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)256338918e0bSBorislav Petkov (AMD) static ssize_t vmpl_show(struct kobject *kobj,
256438918e0bSBorislav Petkov (AMD) struct kobj_attribute *attr, char *buf)
256538918e0bSBorislav Petkov (AMD) {
256638918e0bSBorislav Petkov (AMD) return sysfs_emit(buf, "%d\n", snp_vmpl);
256738918e0bSBorislav Petkov (AMD) }
256838918e0bSBorislav Petkov (AMD)
256938918e0bSBorislav Petkov (AMD) static struct kobj_attribute vmpl_attr = __ATTR_RO(vmpl);
257038918e0bSBorislav Petkov (AMD)
257138918e0bSBorislav Petkov (AMD) static struct attribute *vmpl_attrs[] = {
257238918e0bSBorislav Petkov (AMD) &vmpl_attr.attr,
257338918e0bSBorislav Petkov (AMD) NULL
257438918e0bSBorislav Petkov (AMD) };
257538918e0bSBorislav Petkov (AMD)
257638918e0bSBorislav Petkov (AMD) static struct attribute_group sev_attr_group = {
257738918e0bSBorislav Petkov (AMD) .attrs = vmpl_attrs,
257838918e0bSBorislav Petkov (AMD) };
257938918e0bSBorislav Petkov (AMD)
sev_sysfs_init(void)258038918e0bSBorislav Petkov (AMD) static int __init sev_sysfs_init(void)
258138918e0bSBorislav Petkov (AMD) {
258238918e0bSBorislav Petkov (AMD) struct kobject *sev_kobj;
258338918e0bSBorislav Petkov (AMD) struct device *dev_root;
258438918e0bSBorislav Petkov (AMD) int ret;
258538918e0bSBorislav Petkov (AMD)
258638918e0bSBorislav Petkov (AMD) if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
258738918e0bSBorislav Petkov (AMD) return -ENODEV;
258838918e0bSBorislav Petkov (AMD)
258938918e0bSBorislav Petkov (AMD) dev_root = bus_get_dev_root(&cpu_subsys);
259038918e0bSBorislav Petkov (AMD) if (!dev_root)
259138918e0bSBorislav Petkov (AMD) return -ENODEV;
259238918e0bSBorislav Petkov (AMD)
259338918e0bSBorislav Petkov (AMD) sev_kobj = kobject_create_and_add("sev", &dev_root->kobj);
259438918e0bSBorislav Petkov (AMD) put_device(dev_root);
259538918e0bSBorislav Petkov (AMD)
259638918e0bSBorislav Petkov (AMD) if (!sev_kobj)
259738918e0bSBorislav Petkov (AMD) return -ENOMEM;
259838918e0bSBorislav Petkov (AMD)
259938918e0bSBorislav Petkov (AMD) ret = sysfs_create_group(sev_kobj, &sev_attr_group);
260038918e0bSBorislav Petkov (AMD) if (ret)
260138918e0bSBorislav Petkov (AMD) kobject_put(sev_kobj);
260238918e0bSBorislav Petkov (AMD)
260338918e0bSBorislav Petkov (AMD) return ret;
260438918e0bSBorislav Petkov (AMD) }
260538918e0bSBorislav Petkov (AMD) arch_initcall(sev_sysfs_init);
260638918e0bSBorislav Petkov (AMD) #endif // CONFIG_SYSFS
2607