Commit d5af44dd authored by Brijesh Singh's avatar Brijesh Singh Committed by Borislav Petkov
Browse files

x86/sev: Provide support for SNP guest request NAEs



Version 2 of GHCB specification provides SNP_GUEST_REQUEST and
SNP_EXT_GUEST_REQUEST NAE that can be used by the SNP guest to
communicate with the PSP.

While at it, add a snp_issue_guest_request() helper that will be used by
driver or other subsystem to issue the request to PSP.

See SEV-SNP firmware and GHCB spec for more details.

Signed-off-by: default avatarBrijesh Singh <brijesh.singh@amd.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Link: https://lore.kernel.org/r/20220307213356.2797205-42-brijesh.singh@amd.com
parent ba37a143
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -128,6 +128,9 @@ struct snp_psc_desc {
	struct psc_entry entries[VMGEXIT_PSC_MAX_ENTRY];
} __packed;

/* Guest message request error code */
#define SNP_GUEST_REQ_INVALID_LEN	BIT_ULL(32)

#define GHCB_MSR_TERM_REQ		0x100
#define GHCB_MSR_TERM_REASON_SET_POS	12
#define GHCB_MSR_TERM_REASON_SET_MASK	0xf
+14 −0
Original line number Diff line number Diff line
@@ -87,6 +87,14 @@ extern bool handle_vc_boot_ghcb(struct pt_regs *regs);

#define RMPADJUST_VMSA_PAGE_BIT		BIT(16)

/* SNP Guest message request */
struct snp_req_data {
	unsigned long req_gpa;
	unsigned long resp_gpa;
	unsigned long data_gpa;
	unsigned int data_npages;
};

#ifdef CONFIG_AMD_MEM_ENCRYPT
extern struct static_key_false sev_es_enable_key;
extern void __sev_es_ist_enter(struct pt_regs *regs);
@@ -154,6 +162,7 @@ void snp_set_memory_private(unsigned long vaddr, unsigned int npages);
void snp_set_wakeup_secondary_cpu(void);
bool snp_init(struct boot_params *bp);
void snp_abort(void);
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err);
#else
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
@@ -173,6 +182,11 @@ static inline void snp_set_memory_private(unsigned long vaddr, unsigned int npag
static inline void snp_set_wakeup_secondary_cpu(void) { }
static inline bool snp_init(struct boot_params *bp) { return false; }
static inline void snp_abort(void) { }
static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input,
					  unsigned long *fw_err)
{
	return -ENOTTY;
}
#endif

#endif
+4 −0
Original line number Diff line number Diff line
@@ -109,6 +109,8 @@
#define SVM_VMGEXIT_SET_AP_JUMP_TABLE		0
#define SVM_VMGEXIT_GET_AP_JUMP_TABLE		1
#define SVM_VMGEXIT_PSC				0x80000010
#define SVM_VMGEXIT_GUEST_REQUEST		0x80000011
#define SVM_VMGEXIT_EXT_GUEST_REQUEST		0x80000012
#define SVM_VMGEXIT_AP_CREATION			0x80000013
#define SVM_VMGEXIT_AP_CREATE_ON_INIT		0
#define SVM_VMGEXIT_AP_CREATE			1
@@ -225,6 +227,8 @@
	{ SVM_VMGEXIT_AP_HLT_LOOP,	"vmgexit_ap_hlt_loop" }, \
	{ SVM_VMGEXIT_AP_JUMP_TABLE,	"vmgexit_ap_jump_table" }, \
	{ SVM_VMGEXIT_PSC,		"vmgexit_page_state_change" }, \
	{ SVM_VMGEXIT_GUEST_REQUEST,	"vmgexit_guest_request" }, \
	{ SVM_VMGEXIT_EXT_GUEST_REQUEST, "vmgexit_ext_guest_request" }, \
	{ SVM_VMGEXIT_AP_CREATION,	"vmgexit_ap_creation" }, \
	{ SVM_VMGEXIT_HV_FEATURES,	"vmgexit_hypervisor_feature" }, \
	{ SVM_EXIT_ERR,         "invalid_guest_state" }
+57 −0
Original line number Diff line number Diff line
@@ -2106,3 +2106,60 @@ static int __init init_sev_config(char *str)
	return 1;
}
__setup("sev=", init_sev_config);

int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err)
{
	struct ghcb_state state;
	struct es_em_ctxt ctxt;
	unsigned long flags;
	struct ghcb *ghcb;
	int ret;

	if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
		return -ENODEV;

	if (!fw_err)
		return -EINVAL;

	/*
	 * __sev_get_ghcb() needs to run with IRQs disabled because it is using
	 * a per-CPU GHCB.
	 */
	local_irq_save(flags);

	ghcb = __sev_get_ghcb(&state);
	if (!ghcb) {
		ret = -EIO;
		goto e_restore_irq;
	}

	vc_ghcb_invalidate(ghcb);

	if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
		ghcb_set_rax(ghcb, input->data_gpa);
		ghcb_set_rbx(ghcb, input->data_npages);
	}

	ret = sev_es_ghcb_hv_call(ghcb, true, &ctxt, exit_code, input->req_gpa, input->resp_gpa);
	if (ret)
		goto e_put;

	if (ghcb->save.sw_exit_info_2) {
		/* Number of expected pages are returned in RBX */
		if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST &&
		    ghcb->save.sw_exit_info_2 == SNP_GUEST_REQ_INVALID_LEN)
			input->data_npages = ghcb_get_rbx(ghcb);

		*fw_err = ghcb->save.sw_exit_info_2;

		ret = -EIO;
	}

e_put:
	__sev_put_ghcb(&state);
e_restore_irq:
	local_irq_restore(flags);

	return ret;
}
EXPORT_SYMBOL_GPL(snp_issue_guest_request);