Commit 14bed9bc authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'x86_sev_for_v6.17_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 SEV updates from Borislav Petkov:

 - Map the SNP calling area pages too so that OVMF EFI fw can issue SVSM
   calls properly with the goal of implementing EFI variable store in
   the SVSM - a component which is trusted by the guest, vs in the
   firmware, which is not

 - Allow the kernel to handle #VC exceptions from EFI runtime services
   properly when running as a SNP guest

 - Rework and cleanup the SNP guest request issue glue code a bit

* tag 'x86_sev_for_v6.17_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/sev: Let sev_es_efi_map_ghcbs() map the CA pages too
  x86/sev/vc: Fix EFI runtime instruction emulation
  x86/sev: Drop unnecessary parameter in snp_issue_guest_request()
  x86/sev: Document requirement for linear mapping of guest request buffers
  x86/sev: Allocate request in TSC_INFO_REQ on stack
  virt: sev-guest: Contain snp_guest_request_ioctl in sev-guest
parents 01fce21e a7549636
Loading
Loading
Loading
Loading
+48 −41
Original line number Diff line number Diff line
@@ -1045,11 +1045,13 @@ int __init sev_es_setup_ap_jump_table(struct real_mode_header *rmh)
 * This is needed by the OVMF UEFI firmware which will use whatever it finds in
 * the GHCB MSR as its GHCB to talk to the hypervisor. So make sure the per-cpu
 * runtime GHCBs used by the kernel are also mapped in the EFI page-table.
 *
 * When running under SVSM the CA page is needed too, so map it as well.
 */
int __init sev_es_efi_map_ghcbs(pgd_t *pgd)
int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd)
{
	unsigned long address, pflags, pflags_enc;
	struct sev_es_runtime_data *data;
	unsigned long address, pflags;
	int cpu;
	u64 pfn;

@@ -1057,6 +1059,7 @@ int __init sev_es_efi_map_ghcbs(pgd_t *pgd)
		return 0;

	pflags = _PAGE_NX | _PAGE_RW;
	pflags_enc = cc_mkenc(pflags);

	for_each_possible_cpu(cpu) {
		data = per_cpu(runtime_data, cpu);
@@ -1066,6 +1069,16 @@ int __init sev_es_efi_map_ghcbs(pgd_t *pgd)

		if (kernel_map_pages_in_pgd(pgd, pfn, address, 1, pflags))
			return 1;

		if (snp_vmpl) {
			address = per_cpu(svsm_caa_pa, cpu);
			if (!address)
				return 1;

			pfn = address >> PAGE_SHIFT;
			if (kernel_map_pages_in_pgd(pgd, pfn, address, 1, pflags_enc))
				return 1;
		}
	}

	return 0;
@@ -1389,16 +1402,16 @@ int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call,
}
EXPORT_SYMBOL_GPL(snp_issue_svsm_attest_req);

static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
				   struct snp_guest_request_ioctl *rio)
static int snp_issue_guest_request(struct snp_guest_req *req)
{
	struct snp_req_data *input = &req->input;
	struct ghcb_state state;
	struct es_em_ctxt ctxt;
	unsigned long flags;
	struct ghcb *ghcb;
	int ret;

	rio->exitinfo2 = SEV_RET_NO_FW_CALL;
	req->exitinfo2 = SEV_RET_NO_FW_CALL;

	/*
	 * __sev_get_ghcb() needs to run with IRQs disabled because it is using
@@ -1423,8 +1436,8 @@ static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_dat
	if (ret)
		goto e_put;

	rio->exitinfo2 = ghcb->save.sw_exit_info_2;
	switch (rio->exitinfo2) {
	req->exitinfo2 = ghcb->save.sw_exit_info_2;
	switch (req->exitinfo2) {
	case 0:
		break;

@@ -1919,8 +1932,7 @@ static int enc_payload(struct snp_msg_desc *mdesc, u64 seqno, struct snp_guest_r
	return 0;
}

static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
				  struct snp_guest_request_ioctl *rio)
static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req)
{
	unsigned long req_start = jiffies;
	unsigned int override_npages = 0;
@@ -1934,7 +1946,7 @@ static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_r
	 * sequence number must be incremented or the VMPCK must be deleted to
	 * prevent reuse of the IV.
	 */
	rc = snp_issue_guest_request(req, &req->input, rio);
	rc = snp_issue_guest_request(req);
	switch (rc) {
	case -ENOSPC:
		/*
@@ -1987,7 +1999,7 @@ static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_r
	snp_inc_msg_seqno(mdesc);

	if (override_err) {
		rio->exitinfo2 = override_err;
		req->exitinfo2 = override_err;

		/*
		 * If an extended guest request was issued and the supplied certificate
@@ -2005,12 +2017,20 @@ static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_r
	return rc;
}

int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
			   struct snp_guest_request_ioctl *rio)
int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req)
{
	u64 seqno;
	int rc;

	/*
	 * enc_payload() calls aesgcm_encrypt(), which can potentially offload to HW.
	 * The offload's DMA SG list of data to encrypt has to be in linear mapping.
	 */
	if (!virt_addr_valid(req->req_buf) || !virt_addr_valid(req->resp_buf)) {
		pr_warn("AES-GSM buffers must be in linear mapping");
		return -EINVAL;
	}

	guard(mutex)(&snp_cmd_mutex);

	/* Check if the VMPCK is not empty */
@@ -2043,14 +2063,14 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req
	req->input.resp_gpa = __pa(mdesc->response);
	req->input.data_gpa = req->certs_data ? __pa(req->certs_data) : 0;

	rc = __handle_guest_request(mdesc, req, rio);
	rc = __handle_guest_request(mdesc, req);
	if (rc) {
		if (rc == -EIO &&
		    rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
		    req->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
			return rc;

		pr_alert("Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
			 rc, rio->exitinfo2);
			 rc, req->exitinfo2);

		snp_disable_vmpck(mdesc);
		return rc;
@@ -2069,11 +2089,10 @@ EXPORT_SYMBOL_GPL(snp_send_guest_request);

static int __init snp_get_tsc_info(void)
{
	struct snp_guest_request_ioctl *rio;
	struct snp_tsc_info_resp *tsc_resp;
	struct snp_tsc_info_req *tsc_req;
	struct snp_msg_desc *mdesc;
	struct snp_guest_req *req;
	struct snp_guest_req req = {};
	int rc = -ENOMEM;

	tsc_req = kzalloc(sizeof(*tsc_req), GFP_KERNEL);
@@ -2089,32 +2108,24 @@ static int __init snp_get_tsc_info(void)
	if (!tsc_resp)
		goto e_free_tsc_req;

	req = kzalloc(sizeof(*req), GFP_KERNEL);
	if (!req)
		goto e_free_tsc_resp;

	rio = kzalloc(sizeof(*rio), GFP_KERNEL);
	if (!rio)
		goto e_free_req;

	mdesc = snp_msg_alloc();
	if (IS_ERR_OR_NULL(mdesc))
		goto e_free_rio;
		goto e_free_tsc_resp;

	rc = snp_msg_init(mdesc, snp_vmpl);
	if (rc)
		goto e_free_mdesc;

	req->msg_version = MSG_HDR_VER;
	req->msg_type = SNP_MSG_TSC_INFO_REQ;
	req->vmpck_id = snp_vmpl;
	req->req_buf = tsc_req;
	req->req_sz = sizeof(*tsc_req);
	req->resp_buf = (void *)tsc_resp;
	req->resp_sz = sizeof(*tsc_resp) + AUTHTAG_LEN;
	req->exit_code = SVM_VMGEXIT_GUEST_REQUEST;
	req.msg_version = MSG_HDR_VER;
	req.msg_type = SNP_MSG_TSC_INFO_REQ;
	req.vmpck_id = snp_vmpl;
	req.req_buf = tsc_req;
	req.req_sz = sizeof(*tsc_req);
	req.resp_buf = (void *)tsc_resp;
	req.resp_sz = sizeof(*tsc_resp) + AUTHTAG_LEN;
	req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;

	rc = snp_send_guest_request(mdesc, req, rio);
	rc = snp_send_guest_request(mdesc, &req);
	if (rc)
		goto e_request;

@@ -2135,10 +2146,6 @@ static int __init snp_get_tsc_info(void)
	memzero_explicit(tsc_resp, sizeof(*tsc_resp) + AUTHTAG_LEN);
e_free_mdesc:
	snp_msg_free(mdesc);
e_free_rio:
	kfree(rio);
e_free_req:
	kfree(req);
e_free_tsc_resp:
	kfree(tsc_resp);
e_free_tsc_req:
+8 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/psp-sev.h>
#include <linux/efi.h>
#include <uapi/linux/sev-guest.h>

#include <asm/init.h>
@@ -178,9 +179,15 @@ static enum es_result __vc_decode_kern_insn(struct es_em_ctxt *ctxt)
		return ES_OK;
}

/*
 * User instruction decoding is also required for the EFI runtime. Even though
 * the EFI runtime is running in kernel mode, it uses special EFI virtual
 * address mappings that require the use of efi_mm to properly address and
 * decode.
 */
static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
{
	if (user_mode(ctxt->regs))
	if (user_mode(ctxt->regs) || mm_is_efi(current->active_mm))
		return __vc_decode_user_insn(ctxt);
	else
		return __vc_decode_kern_insn(ctxt);
+6 −8
Original line number Diff line number Diff line
@@ -243,6 +243,7 @@ struct snp_guest_req {
	size_t resp_sz;

	u64 exit_code;
	u64 exitinfo2;
	unsigned int vmpck_id;
	u8 msg_version;
	u8 msg_type;
@@ -460,7 +461,7 @@ static __always_inline void sev_es_nmi_complete(void)
	    cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
		__sev_es_nmi_complete();
}
extern int __init sev_es_efi_map_ghcbs(pgd_t *pgd);
extern int __init sev_es_efi_map_ghcbs_cas(pgd_t *pgd);
extern void sev_enable(struct boot_params *bp);

/*
@@ -501,8 +502,6 @@ static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate)
	return rc;
}

struct snp_guest_request_ioctl;

void setup_ghcb(void);
void early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
				  unsigned long npages);
@@ -528,8 +527,7 @@ void snp_kexec_begin(void);
int snp_msg_init(struct snp_msg_desc *mdesc, int vmpck_id);
struct snp_msg_desc *snp_msg_alloc(void);
void snp_msg_free(struct snp_msg_desc *mdesc);
int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
			   struct snp_guest_request_ioctl *rio);
int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req);

int snp_svsm_vtpm_send_command(u8 *buffer);

@@ -571,7 +569,7 @@ static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
static inline int sev_es_setup_ap_jump_table(struct real_mode_header *rmh) { return 0; }
static inline void sev_es_nmi_complete(void) { }
static inline int sev_es_efi_map_ghcbs(pgd_t *pgd) { return 0; }
static inline int sev_es_efi_map_ghcbs_cas(pgd_t *pgd) { return 0; }
static inline void sev_enable(struct boot_params *bp) { }
static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate) { return 0; }
static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long attrs) { return 0; }
@@ -602,8 +600,8 @@ static inline void snp_kexec_begin(void) { }
static inline int snp_msg_init(struct snp_msg_desc *mdesc, int vmpck_id) { return -1; }
static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; }
static inline void snp_msg_free(struct snp_msg_desc *mdesc) { }
static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
					 struct snp_guest_request_ioctl *rio) { return -ENODEV; }
static inline int snp_send_guest_request(struct snp_msg_desc *mdesc,
					 struct snp_guest_req *req) { return -ENODEV; }
static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; }
static inline void __init snp_secure_tsc_prepare(void) { }
static inline void __init snp_secure_tsc_init(void) { }
+2 −2
Original line number Diff line number Diff line
@@ -216,8 +216,8 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
	 * When SEV-ES is active, the GHCB as set by the kernel will be used
	 * by firmware. Create a 1:1 unencrypted mapping for each GHCB.
	 */
	if (sev_es_efi_map_ghcbs(pgd)) {
		pr_err("Failed to create 1:1 mapping for the GHCBs!\n");
	if (sev_es_efi_map_ghcbs_cas(pgd)) {
		pr_err("Failed to create 1:1 mapping for the GHCBs and CAs!\n");
		return 1;
	}

+6 −3
Original line number Diff line number Diff line
@@ -101,7 +101,8 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
	req.resp_sz = resp_len;
	req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;

	rc = snp_send_guest_request(mdesc, &req, arg);
	rc = snp_send_guest_request(mdesc, &req);
	arg->exitinfo2 = req.exitinfo2;
	if (rc)
		goto e_free;

@@ -152,7 +153,8 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
	req.resp_sz = resp_len;
	req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;

	rc = snp_send_guest_request(mdesc, &req, arg);
	rc = snp_send_guest_request(mdesc, &req);
	arg->exitinfo2 = req.exitinfo2;
	if (rc)
		return rc;

@@ -249,7 +251,8 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
	req.resp_sz = resp_len;
	req.exit_code = SVM_VMGEXIT_EXT_GUEST_REQUEST;

	ret = snp_send_guest_request(mdesc, &req, arg);
	ret = snp_send_guest_request(mdesc, &req);
	arg->exitinfo2 = req.exitinfo2;

	/* If certs length is invalid then copy the returned length */
	if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) {