x86/sev: Provide support for SNP guest request NAEs
authorBrijesh Singh <brijesh.singh@amd.com>
Mon, 7 Mar 2022 21:33:51 +0000 (15:33 -0600)
committerBorislav Petkov <bp@suse.de>
Thu, 7 Apr 2022 14:47:12 +0000 (16:47 +0200)
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: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lore.kernel.org/r/20220307213356.2797205-42-brijesh.singh@amd.com
arch/x86/include/asm/sev-common.h
arch/x86/include/asm/sev.h
arch/x86/include/uapi/asm/svm.h
arch/x86/kernel/sev.c

index 0759af9..b8357d6 100644 (file)
@@ -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
index 84d3d51..c63a1d4 100644 (file)
@@ -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
index cfea5e8..f69c168 100644 (file)
 #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
        { 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" }
index 70ecc6e..7237b41 100644 (file)
@@ -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);