mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	virt: Add SEV-SNP guest driver
The SEV-SNP specification provides the guest a mechanism to communicate with the PSP without risk from a malicious hypervisor who wishes to read, alter, drop or replay the messages sent. The driver uses snp_issue_guest_request() to issue GHCB SNP_GUEST_REQUEST or SNP_EXT_GUEST_REQUEST NAE events to submit the request to PSP. The PSP requires that all communication should be encrypted using key specified through a struct snp_guest_platform_data descriptor. Userspace can use SNP_GET_REPORT ioctl() to query the guest attestation report. See SEV-SNP spec section Guest Messages for more details. [ bp: Remove the "what" from the commit message, massage. ] 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-44-brijesh.singh@amd.com
This commit is contained in:
		
							parent
							
								
									3a45b37538
								
							
						
					
					
						commit
						fce96cf044
					
				
					 9 changed files with 862 additions and 0 deletions
				
			
		
							
								
								
									
										86
									
								
								Documentation/virt/coco/sevguest.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								Documentation/virt/coco/sevguest.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
.. SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
 | 
			
		||||
===================================================================
 | 
			
		||||
The Definitive SEV Guest API Documentation
 | 
			
		||||
===================================================================
 | 
			
		||||
 | 
			
		||||
1. General description
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
The SEV API is a set of ioctls that are used by the guest or hypervisor
 | 
			
		||||
to get or set a certain aspect of the SEV virtual machine. The ioctls belong
 | 
			
		||||
to the following classes:
 | 
			
		||||
 | 
			
		||||
 - Hypervisor ioctls: These query and set global attributes which affect the
 | 
			
		||||
   whole SEV firmware.  These ioctl are used by platform provisioning tools.
 | 
			
		||||
 | 
			
		||||
 - Guest ioctls: These query and set attributes of the SEV virtual machine.
 | 
			
		||||
 | 
			
		||||
2. API description
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
This section describes ioctls that is used for querying the SEV guest report
 | 
			
		||||
from the SEV firmware. For each ioctl, the following information is provided
 | 
			
		||||
along with a description:
 | 
			
		||||
 | 
			
		||||
  Technology:
 | 
			
		||||
      which SEV technology provides this ioctl. SEV, SEV-ES, SEV-SNP or all.
 | 
			
		||||
 | 
			
		||||
  Type:
 | 
			
		||||
      hypervisor or guest. The ioctl can be used inside the guest or the
 | 
			
		||||
      hypervisor.
 | 
			
		||||
 | 
			
		||||
  Parameters:
 | 
			
		||||
      what parameters are accepted by the ioctl.
 | 
			
		||||
 | 
			
		||||
  Returns:
 | 
			
		||||
      the return value.  General error numbers (-ENOMEM, -EINVAL)
 | 
			
		||||
      are not detailed, but errors with specific meanings are.
 | 
			
		||||
 | 
			
		||||
The guest ioctl should be issued on a file descriptor of the /dev/sev-guest device.
 | 
			
		||||
The ioctl accepts struct snp_user_guest_request. The input and output structure is
 | 
			
		||||
specified through the req_data and resp_data field respectively. If the ioctl fails
 | 
			
		||||
to execute due to a firmware error, then fw_err code will be set otherwise the
 | 
			
		||||
fw_err will be set to 0x00000000000000ff.
 | 
			
		||||
 | 
			
		||||
The firmware checks that the message sequence counter is one greater than
 | 
			
		||||
the guests message sequence counter. If guest driver fails to increment message
 | 
			
		||||
counter (e.g. counter overflow), then -EIO will be returned.
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
        struct snp_guest_request_ioctl {
 | 
			
		||||
                /* Message version number */
 | 
			
		||||
                __u32 msg_version;
 | 
			
		||||
 | 
			
		||||
                /* Request and response structure address */
 | 
			
		||||
                __u64 req_data;
 | 
			
		||||
                __u64 resp_data;
 | 
			
		||||
 | 
			
		||||
                /* firmware error code on failure (see psp-sev.h) */
 | 
			
		||||
                __u64 fw_err;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
2.1 SNP_GET_REPORT
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
:Technology: sev-snp
 | 
			
		||||
:Type: guest ioctl
 | 
			
		||||
:Parameters (in): struct snp_report_req
 | 
			
		||||
:Returns (out): struct snp_report_resp on success, -negative on error
 | 
			
		||||
 | 
			
		||||
The SNP_GET_REPORT ioctl can be used to query the attestation report from the
 | 
			
		||||
SEV-SNP firmware. The ioctl uses the SNP_GUEST_REQUEST (MSG_REPORT_REQ) command
 | 
			
		||||
provided by the SEV-SNP firmware to query the attestation report.
 | 
			
		||||
 | 
			
		||||
On success, the snp_report_resp.data will contains the report. The report
 | 
			
		||||
contain the format described in the SEV-SNP specification. See the SEV-SNP
 | 
			
		||||
specification for further details.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Reference
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
SEV-SNP and GHCB specification: developer.amd.com/sev
 | 
			
		||||
 | 
			
		||||
The driver is based on SEV-SNP firmware spec 0.9 and GHCB spec version 2.0.
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ Linux Virtualization Support
 | 
			
		|||
   guest-halt-polling
 | 
			
		||||
   ne_overview
 | 
			
		||||
   acrn/index
 | 
			
		||||
   coco/sevguest
 | 
			
		||||
 | 
			
		||||
.. only:: html and subproject
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,4 +47,7 @@ source "drivers/virt/vboxguest/Kconfig"
 | 
			
		|||
source "drivers/virt/nitro_enclaves/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/virt/acrn/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/virt/coco/sevguest/Kconfig"
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,3 +9,4 @@ obj-y				+= vboxguest/
 | 
			
		|||
 | 
			
		||||
obj-$(CONFIG_NITRO_ENCLAVES)	+= nitro_enclaves/
 | 
			
		||||
obj-$(CONFIG_ACRN_HSM)		+= acrn/
 | 
			
		||||
obj-$(CONFIG_SEV_GUEST)		+= coco/sevguest/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								drivers/virt/coco/sevguest/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								drivers/virt/coco/sevguest/Kconfig
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
config SEV_GUEST
 | 
			
		||||
	tristate "AMD SEV Guest driver"
 | 
			
		||||
	default m
 | 
			
		||||
	depends on AMD_MEM_ENCRYPT
 | 
			
		||||
	select CRYPTO_AEAD2
 | 
			
		||||
	select CRYPTO_GCM
 | 
			
		||||
	help
 | 
			
		||||
	  SEV-SNP firmware provides the guest a mechanism to communicate with
 | 
			
		||||
	  the PSP without risk from a malicious hypervisor who wishes to read,
 | 
			
		||||
	  alter, drop or replay the messages sent. The driver provides
 | 
			
		||||
	  userspace interface to communicate with the PSP to request the
 | 
			
		||||
	  attestation report and more.
 | 
			
		||||
 | 
			
		||||
	  If you choose 'M' here, this module will be called sevguest.
 | 
			
		||||
							
								
								
									
										2
									
								
								drivers/virt/coco/sevguest/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								drivers/virt/coco/sevguest/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
# SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
obj-$(CONFIG_SEV_GUEST) += sevguest.o
 | 
			
		||||
							
								
								
									
										607
									
								
								drivers/virt/coco/sevguest/sevguest.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										607
									
								
								drivers/virt/coco/sevguest/sevguest.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,607 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * AMD Secure Encrypted Virtualization Nested Paging (SEV-SNP) guest request interface
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 Advanced Micro Devices, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Brijesh Singh <brijesh.singh@amd.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt) "SNP: GUEST: " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mutex.h>
 | 
			
		||||
#include <linux/io.h>
 | 
			
		||||
#include <linux/platform_device.h>
 | 
			
		||||
#include <linux/miscdevice.h>
 | 
			
		||||
#include <linux/set_memory.h>
 | 
			
		||||
#include <linux/fs.h>
 | 
			
		||||
#include <crypto/aead.h>
 | 
			
		||||
#include <linux/scatterlist.h>
 | 
			
		||||
#include <linux/psp-sev.h>
 | 
			
		||||
#include <uapi/linux/sev-guest.h>
 | 
			
		||||
#include <uapi/linux/psp-sev.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/svm.h>
 | 
			
		||||
#include <asm/sev.h>
 | 
			
		||||
 | 
			
		||||
#include "sevguest.h"
 | 
			
		||||
 | 
			
		||||
#define DEVICE_NAME	"sev-guest"
 | 
			
		||||
#define AAD_LEN		48
 | 
			
		||||
#define MSG_HDR_VER	1
 | 
			
		||||
 | 
			
		||||
struct snp_guest_crypto {
 | 
			
		||||
	struct crypto_aead *tfm;
 | 
			
		||||
	u8 *iv, *authtag;
 | 
			
		||||
	int iv_len, a_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct snp_guest_dev {
 | 
			
		||||
	struct device *dev;
 | 
			
		||||
	struct miscdevice misc;
 | 
			
		||||
 | 
			
		||||
	struct snp_guest_crypto *crypto;
 | 
			
		||||
	struct snp_guest_msg *request, *response;
 | 
			
		||||
	struct snp_secrets_page_layout *layout;
 | 
			
		||||
	struct snp_req_data input;
 | 
			
		||||
	u32 *os_area_msg_seqno;
 | 
			
		||||
	u8 *vmpck;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static u32 vmpck_id;
 | 
			
		||||
module_param(vmpck_id, uint, 0444);
 | 
			
		||||
MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.");
 | 
			
		||||
 | 
			
		||||
/* Mutex to serialize the shared buffer access and command handling. */
 | 
			
		||||
static DEFINE_MUTEX(snp_cmd_mutex);
 | 
			
		||||
 | 
			
		||||
static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
 | 
			
		||||
{
 | 
			
		||||
	char zero_key[VMPCK_KEY_LEN] = {0};
 | 
			
		||||
 | 
			
		||||
	if (snp_dev->vmpck)
 | 
			
		||||
		return !memcmp(snp_dev->vmpck, zero_key, VMPCK_KEY_LEN);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
 | 
			
		||||
{
 | 
			
		||||
	memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN);
 | 
			
		||||
	snp_dev->vmpck = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
 | 
			
		||||
{
 | 
			
		||||
	u64 count;
 | 
			
		||||
 | 
			
		||||
	lockdep_assert_held(&snp_cmd_mutex);
 | 
			
		||||
 | 
			
		||||
	/* Read the current message sequence counter from secrets pages */
 | 
			
		||||
	count = *snp_dev->os_area_msg_seqno;
 | 
			
		||||
 | 
			
		||||
	return count + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return a non-zero on success */
 | 
			
		||||
static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
 | 
			
		||||
{
 | 
			
		||||
	u64 count = __snp_get_msg_seqno(snp_dev);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The message sequence counter for the SNP guest request is a  64-bit
 | 
			
		||||
	 * value but the version 2 of GHCB specification defines a 32-bit storage
 | 
			
		||||
	 * for it. If the counter exceeds the 32-bit value then return zero.
 | 
			
		||||
	 * The caller should check the return value, but if the caller happens to
 | 
			
		||||
	 * not check the value and use it, then the firmware treats zero as an
 | 
			
		||||
	 * invalid number and will fail the  message request.
 | 
			
		||||
	 */
 | 
			
		||||
	if (count >= UINT_MAX) {
 | 
			
		||||
		dev_err(snp_dev->dev, "request message sequence counter overflow\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * The counter is also incremented by the PSP, so increment it by 2
 | 
			
		||||
	 * and save in secrets page.
 | 
			
		||||
	 */
 | 
			
		||||
	*snp_dev->os_area_msg_seqno += 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct snp_guest_dev *to_snp_dev(struct file *file)
 | 
			
		||||
{
 | 
			
		||||
	struct miscdevice *dev = file->private_data;
 | 
			
		||||
 | 
			
		||||
	return container_of(dev, struct snp_guest_dev, misc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct snp_guest_crypto *init_crypto(struct snp_guest_dev *snp_dev, u8 *key, size_t keylen)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_crypto *crypto;
 | 
			
		||||
 | 
			
		||||
	crypto = kzalloc(sizeof(*crypto), GFP_KERNEL_ACCOUNT);
 | 
			
		||||
	if (!crypto)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	crypto->tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
 | 
			
		||||
	if (IS_ERR(crypto->tfm))
 | 
			
		||||
		goto e_free;
 | 
			
		||||
 | 
			
		||||
	if (crypto_aead_setkey(crypto->tfm, key, keylen))
 | 
			
		||||
		goto e_free_crypto;
 | 
			
		||||
 | 
			
		||||
	crypto->iv_len = crypto_aead_ivsize(crypto->tfm);
 | 
			
		||||
	crypto->iv = kmalloc(crypto->iv_len, GFP_KERNEL_ACCOUNT);
 | 
			
		||||
	if (!crypto->iv)
 | 
			
		||||
		goto e_free_crypto;
 | 
			
		||||
 | 
			
		||||
	if (crypto_aead_authsize(crypto->tfm) > MAX_AUTHTAG_LEN) {
 | 
			
		||||
		if (crypto_aead_setauthsize(crypto->tfm, MAX_AUTHTAG_LEN)) {
 | 
			
		||||
			dev_err(snp_dev->dev, "failed to set authsize to %d\n", MAX_AUTHTAG_LEN);
 | 
			
		||||
			goto e_free_iv;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	crypto->a_len = crypto_aead_authsize(crypto->tfm);
 | 
			
		||||
	crypto->authtag = kmalloc(crypto->a_len, GFP_KERNEL_ACCOUNT);
 | 
			
		||||
	if (!crypto->authtag)
 | 
			
		||||
		goto e_free_auth;
 | 
			
		||||
 | 
			
		||||
	return crypto;
 | 
			
		||||
 | 
			
		||||
e_free_auth:
 | 
			
		||||
	kfree(crypto->authtag);
 | 
			
		||||
e_free_iv:
 | 
			
		||||
	kfree(crypto->iv);
 | 
			
		||||
e_free_crypto:
 | 
			
		||||
	crypto_free_aead(crypto->tfm);
 | 
			
		||||
e_free:
 | 
			
		||||
	kfree(crypto);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void deinit_crypto(struct snp_guest_crypto *crypto)
 | 
			
		||||
{
 | 
			
		||||
	crypto_free_aead(crypto->tfm);
 | 
			
		||||
	kfree(crypto->iv);
 | 
			
		||||
	kfree(crypto->authtag);
 | 
			
		||||
	kfree(crypto);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int enc_dec_message(struct snp_guest_crypto *crypto, struct snp_guest_msg *msg,
 | 
			
		||||
			   u8 *src_buf, u8 *dst_buf, size_t len, bool enc)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_msg_hdr *hdr = &msg->hdr;
 | 
			
		||||
	struct scatterlist src[3], dst[3];
 | 
			
		||||
	DECLARE_CRYPTO_WAIT(wait);
 | 
			
		||||
	struct aead_request *req;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	req = aead_request_alloc(crypto->tfm, GFP_KERNEL);
 | 
			
		||||
	if (!req)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * AEAD memory operations:
 | 
			
		||||
	 * +------ AAD -------+------- DATA -----+---- AUTHTAG----+
 | 
			
		||||
	 * |  msg header      |  plaintext       |  hdr->authtag  |
 | 
			
		||||
	 * | bytes 30h - 5Fh  |    or            |                |
 | 
			
		||||
	 * |                  |   cipher         |                |
 | 
			
		||||
	 * +------------------+------------------+----------------+
 | 
			
		||||
	 */
 | 
			
		||||
	sg_init_table(src, 3);
 | 
			
		||||
	sg_set_buf(&src[0], &hdr->algo, AAD_LEN);
 | 
			
		||||
	sg_set_buf(&src[1], src_buf, hdr->msg_sz);
 | 
			
		||||
	sg_set_buf(&src[2], hdr->authtag, crypto->a_len);
 | 
			
		||||
 | 
			
		||||
	sg_init_table(dst, 3);
 | 
			
		||||
	sg_set_buf(&dst[0], &hdr->algo, AAD_LEN);
 | 
			
		||||
	sg_set_buf(&dst[1], dst_buf, hdr->msg_sz);
 | 
			
		||||
	sg_set_buf(&dst[2], hdr->authtag, crypto->a_len);
 | 
			
		||||
 | 
			
		||||
	aead_request_set_ad(req, AAD_LEN);
 | 
			
		||||
	aead_request_set_tfm(req, crypto->tfm);
 | 
			
		||||
	aead_request_set_callback(req, 0, crypto_req_done, &wait);
 | 
			
		||||
 | 
			
		||||
	aead_request_set_crypt(req, src, dst, len, crypto->iv);
 | 
			
		||||
	ret = crypto_wait_req(enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req), &wait);
 | 
			
		||||
 | 
			
		||||
	aead_request_free(req);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __enc_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
 | 
			
		||||
			 void *plaintext, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_crypto *crypto = snp_dev->crypto;
 | 
			
		||||
	struct snp_guest_msg_hdr *hdr = &msg->hdr;
 | 
			
		||||
 | 
			
		||||
	memset(crypto->iv, 0, crypto->iv_len);
 | 
			
		||||
	memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
 | 
			
		||||
 | 
			
		||||
	return enc_dec_message(crypto, msg, plaintext, msg->payload, len, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
 | 
			
		||||
		       void *plaintext, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_crypto *crypto = snp_dev->crypto;
 | 
			
		||||
	struct snp_guest_msg_hdr *hdr = &msg->hdr;
 | 
			
		||||
 | 
			
		||||
	/* Build IV with response buffer sequence number */
 | 
			
		||||
	memset(crypto->iv, 0, crypto->iv_len);
 | 
			
		||||
	memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
 | 
			
		||||
 | 
			
		||||
	return enc_dec_message(crypto, msg, msg->payload, plaintext, len, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_crypto *crypto = snp_dev->crypto;
 | 
			
		||||
	struct snp_guest_msg *resp = snp_dev->response;
 | 
			
		||||
	struct snp_guest_msg *req = snp_dev->request;
 | 
			
		||||
	struct snp_guest_msg_hdr *req_hdr = &req->hdr;
 | 
			
		||||
	struct snp_guest_msg_hdr *resp_hdr = &resp->hdr;
 | 
			
		||||
 | 
			
		||||
	dev_dbg(snp_dev->dev, "response [seqno %lld type %d version %d sz %d]\n",
 | 
			
		||||
		resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version, resp_hdr->msg_sz);
 | 
			
		||||
 | 
			
		||||
	/* Verify that the sequence counter is incremented by 1 */
 | 
			
		||||
	if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1)))
 | 
			
		||||
		return -EBADMSG;
 | 
			
		||||
 | 
			
		||||
	/* Verify response message type and version number. */
 | 
			
		||||
	if (resp_hdr->msg_type != (req_hdr->msg_type + 1) ||
 | 
			
		||||
	    resp_hdr->msg_version != req_hdr->msg_version)
 | 
			
		||||
		return -EBADMSG;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the message size is greater than our buffer length then return
 | 
			
		||||
	 * an error.
 | 
			
		||||
	 */
 | 
			
		||||
	if (unlikely((resp_hdr->msg_sz + crypto->a_len) > sz))
 | 
			
		||||
		return -EBADMSG;
 | 
			
		||||
 | 
			
		||||
	/* Decrypt the payload */
 | 
			
		||||
	return dec_payload(snp_dev, resp, payload, resp_hdr->msg_sz + crypto->a_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
 | 
			
		||||
			void *payload, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_msg *req = snp_dev->request;
 | 
			
		||||
	struct snp_guest_msg_hdr *hdr = &req->hdr;
 | 
			
		||||
 | 
			
		||||
	memset(req, 0, sizeof(*req));
 | 
			
		||||
 | 
			
		||||
	hdr->algo = SNP_AEAD_AES_256_GCM;
 | 
			
		||||
	hdr->hdr_version = MSG_HDR_VER;
 | 
			
		||||
	hdr->hdr_sz = sizeof(*hdr);
 | 
			
		||||
	hdr->msg_type = type;
 | 
			
		||||
	hdr->msg_version = version;
 | 
			
		||||
	hdr->msg_seqno = seqno;
 | 
			
		||||
	hdr->msg_vmpck = vmpck_id;
 | 
			
		||||
	hdr->msg_sz = sz;
 | 
			
		||||
 | 
			
		||||
	/* Verify the sequence number is non-zero */
 | 
			
		||||
	if (!hdr->msg_seqno)
 | 
			
		||||
		return -ENOSR;
 | 
			
		||||
 | 
			
		||||
	dev_dbg(snp_dev->dev, "request [seqno %lld type %d version %d sz %d]\n",
 | 
			
		||||
		hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
 | 
			
		||||
 | 
			
		||||
	return __enc_payload(snp_dev, req, payload, sz);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, int msg_ver,
 | 
			
		||||
				u8 type, void *req_buf, size_t req_sz, void *resp_buf,
 | 
			
		||||
				u32 resp_sz, __u64 *fw_err)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long err;
 | 
			
		||||
	u64 seqno;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* Get message sequence and verify that its a non-zero */
 | 
			
		||||
	seqno = snp_get_msg_seqno(snp_dev);
 | 
			
		||||
	if (!seqno)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
 | 
			
		||||
 | 
			
		||||
	/* Encrypt the userspace provided payload */
 | 
			
		||||
	rc = enc_payload(snp_dev, seqno, msg_ver, type, req_buf, req_sz);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/* Call firmware to process the request */
 | 
			
		||||
	rc = snp_issue_guest_request(exit_code, &snp_dev->input, &err);
 | 
			
		||||
	if (fw_err)
 | 
			
		||||
		*fw_err = err;
 | 
			
		||||
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The verify_and_dec_payload() will fail only if the hypervisor is
 | 
			
		||||
	 * actively modifying the message header or corrupting the encrypted payload.
 | 
			
		||||
	 * This hints that hypervisor is acting in a bad faith. Disable the VMPCK so that
 | 
			
		||||
	 * the key cannot be used for any communication. The key is disabled to ensure
 | 
			
		||||
	 * that AES-GCM does not use the same IV while encrypting the request payload.
 | 
			
		||||
	 */
 | 
			
		||||
	rc = verify_and_dec_payload(snp_dev, resp_buf, resp_sz);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		dev_alert(snp_dev->dev,
 | 
			
		||||
			  "Detected unexpected decode failure, disabling the vmpck_id %d\n",
 | 
			
		||||
			  vmpck_id);
 | 
			
		||||
		snp_disable_vmpck(snp_dev);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Increment to new message sequence after payload decryption was successful. */
 | 
			
		||||
	snp_inc_msg_seqno(snp_dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_crypto *crypto = snp_dev->crypto;
 | 
			
		||||
	struct snp_report_resp *resp;
 | 
			
		||||
	struct snp_report_req req;
 | 
			
		||||
	int rc, resp_len;
 | 
			
		||||
 | 
			
		||||
	lockdep_assert_held(&snp_cmd_mutex);
 | 
			
		||||
 | 
			
		||||
	if (!arg->req_data || !arg->resp_data)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req)))
 | 
			
		||||
		return -EFAULT;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The intermediate response buffer is used while decrypting the
 | 
			
		||||
	 * response payload. Make sure that it has enough space to cover the
 | 
			
		||||
	 * authtag.
 | 
			
		||||
	 */
 | 
			
		||||
	resp_len = sizeof(resp->data) + crypto->a_len;
 | 
			
		||||
	resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
 | 
			
		||||
	if (!resp)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg->msg_version,
 | 
			
		||||
				  SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data,
 | 
			
		||||
				  resp_len, &arg->fw_err);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		goto e_free;
 | 
			
		||||
 | 
			
		||||
	if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp)))
 | 
			
		||||
		rc = -EFAULT;
 | 
			
		||||
 | 
			
		||||
e_free:
 | 
			
		||||
	kfree(resp);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_dev *snp_dev = to_snp_dev(file);
 | 
			
		||||
	void __user *argp = (void __user *)arg;
 | 
			
		||||
	struct snp_guest_request_ioctl input;
 | 
			
		||||
	int ret = -ENOTTY;
 | 
			
		||||
 | 
			
		||||
	if (copy_from_user(&input, argp, sizeof(input)))
 | 
			
		||||
		return -EFAULT;
 | 
			
		||||
 | 
			
		||||
	input.fw_err = 0xff;
 | 
			
		||||
 | 
			
		||||
	/* Message version must be non-zero */
 | 
			
		||||
	if (!input.msg_version)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&snp_cmd_mutex);
 | 
			
		||||
 | 
			
		||||
	/* Check if the VMPCK is not empty */
 | 
			
		||||
	if (is_vmpck_empty(snp_dev)) {
 | 
			
		||||
		dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
 | 
			
		||||
		mutex_unlock(&snp_cmd_mutex);
 | 
			
		||||
		return -ENOTTY;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (ioctl) {
 | 
			
		||||
	case SNP_GET_REPORT:
 | 
			
		||||
		ret = get_report(snp_dev, &input);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mutex_unlock(&snp_cmd_mutex);
 | 
			
		||||
 | 
			
		||||
	if (input.fw_err && copy_to_user(argp, &input, sizeof(input)))
 | 
			
		||||
		return -EFAULT;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void free_shared_pages(void *buf, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!buf)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ret = set_memory_encrypted((unsigned long)buf, npages);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	__free_pages(virt_to_page(buf), get_order(sz));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *alloc_shared_pages(size_t sz)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
 | 
			
		||||
	struct page *page;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	page = alloc_pages(GFP_KERNEL_ACCOUNT, get_order(sz));
 | 
			
		||||
	if (IS_ERR(page))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	ret = set_memory_decrypted((unsigned long)page_address(page), npages);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_err("failed to mark page shared, ret=%d\n", ret);
 | 
			
		||||
		__free_pages(page, get_order(sz));
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return page_address(page);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct file_operations snp_guest_fops = {
 | 
			
		||||
	.owner	= THIS_MODULE,
 | 
			
		||||
	.unlocked_ioctl = snp_guest_ioctl,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno)
 | 
			
		||||
{
 | 
			
		||||
	u8 *key = NULL;
 | 
			
		||||
 | 
			
		||||
	switch (id) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		*seqno = &layout->os_area.msg_seqno_0;
 | 
			
		||||
		key = layout->vmpck0;
 | 
			
		||||
		break;
 | 
			
		||||
	case 1:
 | 
			
		||||
		*seqno = &layout->os_area.msg_seqno_1;
 | 
			
		||||
		key = layout->vmpck1;
 | 
			
		||||
		break;
 | 
			
		||||
	case 2:
 | 
			
		||||
		*seqno = &layout->os_area.msg_seqno_2;
 | 
			
		||||
		key = layout->vmpck2;
 | 
			
		||||
		break;
 | 
			
		||||
	case 3:
 | 
			
		||||
		*seqno = &layout->os_area.msg_seqno_3;
 | 
			
		||||
		key = layout->vmpck3;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init snp_guest_probe(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_secrets_page_layout *layout;
 | 
			
		||||
	struct snp_guest_platform_data *data;
 | 
			
		||||
	struct device *dev = &pdev->dev;
 | 
			
		||||
	struct snp_guest_dev *snp_dev;
 | 
			
		||||
	struct miscdevice *misc;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!dev->platform_data)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	data = (struct snp_guest_platform_data *)dev->platform_data;
 | 
			
		||||
	layout = (__force void *)ioremap_encrypted(data->secrets_gpa, PAGE_SIZE);
 | 
			
		||||
	if (!layout)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	ret = -ENOMEM;
 | 
			
		||||
	snp_dev = devm_kzalloc(&pdev->dev, sizeof(struct snp_guest_dev), GFP_KERNEL);
 | 
			
		||||
	if (!snp_dev)
 | 
			
		||||
		goto e_unmap;
 | 
			
		||||
 | 
			
		||||
	ret = -EINVAL;
 | 
			
		||||
	snp_dev->vmpck = get_vmpck(vmpck_id, layout, &snp_dev->os_area_msg_seqno);
 | 
			
		||||
	if (!snp_dev->vmpck) {
 | 
			
		||||
		dev_err(dev, "invalid vmpck id %d\n", vmpck_id);
 | 
			
		||||
		goto e_unmap;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Verify that VMPCK is not zero. */
 | 
			
		||||
	if (is_vmpck_empty(snp_dev)) {
 | 
			
		||||
		dev_err(dev, "vmpck id %d is null\n", vmpck_id);
 | 
			
		||||
		goto e_unmap;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	platform_set_drvdata(pdev, snp_dev);
 | 
			
		||||
	snp_dev->dev = dev;
 | 
			
		||||
	snp_dev->layout = layout;
 | 
			
		||||
 | 
			
		||||
	/* Allocate the shared page used for the request and response message. */
 | 
			
		||||
	snp_dev->request = alloc_shared_pages(sizeof(struct snp_guest_msg));
 | 
			
		||||
	if (!snp_dev->request)
 | 
			
		||||
		goto e_unmap;
 | 
			
		||||
 | 
			
		||||
	snp_dev->response = alloc_shared_pages(sizeof(struct snp_guest_msg));
 | 
			
		||||
	if (!snp_dev->response)
 | 
			
		||||
		goto e_free_request;
 | 
			
		||||
 | 
			
		||||
	ret = -EIO;
 | 
			
		||||
	snp_dev->crypto = init_crypto(snp_dev, snp_dev->vmpck, VMPCK_KEY_LEN);
 | 
			
		||||
	if (!snp_dev->crypto)
 | 
			
		||||
		goto e_free_response;
 | 
			
		||||
 | 
			
		||||
	misc = &snp_dev->misc;
 | 
			
		||||
	misc->minor = MISC_DYNAMIC_MINOR;
 | 
			
		||||
	misc->name = DEVICE_NAME;
 | 
			
		||||
	misc->fops = &snp_guest_fops;
 | 
			
		||||
 | 
			
		||||
	/* initial the input address for guest request */
 | 
			
		||||
	snp_dev->input.req_gpa = __pa(snp_dev->request);
 | 
			
		||||
	snp_dev->input.resp_gpa = __pa(snp_dev->response);
 | 
			
		||||
 | 
			
		||||
	ret =  misc_register(misc);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto e_free_response;
 | 
			
		||||
 | 
			
		||||
	dev_info(dev, "Initialized SNP guest driver (using vmpck_id %d)\n", vmpck_id);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
e_free_response:
 | 
			
		||||
	free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
 | 
			
		||||
e_free_request:
 | 
			
		||||
	free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
 | 
			
		||||
e_unmap:
 | 
			
		||||
	iounmap(layout);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __exit snp_guest_remove(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct snp_guest_dev *snp_dev = platform_get_drvdata(pdev);
 | 
			
		||||
 | 
			
		||||
	free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
 | 
			
		||||
	free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
 | 
			
		||||
	deinit_crypto(snp_dev->crypto);
 | 
			
		||||
	misc_deregister(&snp_dev->misc);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct platform_driver snp_guest_driver = {
 | 
			
		||||
	.remove		= __exit_p(snp_guest_remove),
 | 
			
		||||
	.driver		= {
 | 
			
		||||
		.name = "snp-guest",
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module_platform_driver_probe(snp_guest_driver, snp_guest_probe);
 | 
			
		||||
 | 
			
		||||
MODULE_AUTHOR("Brijesh Singh <brijesh.singh@amd.com>");
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
MODULE_VERSION("1.0.0");
 | 
			
		||||
MODULE_DESCRIPTION("AMD SNP Guest Driver");
 | 
			
		||||
							
								
								
									
										98
									
								
								drivers/virt/coco/sevguest/sevguest.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								drivers/virt/coco/sevguest/sevguest.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,98 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-only */
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 Advanced Micro Devices, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Brijesh Singh <brijesh.singh@amd.com>
 | 
			
		||||
 *
 | 
			
		||||
 * SEV-SNP API spec is available at https://developer.amd.com/sev
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __VIRT_SEVGUEST_H__
 | 
			
		||||
#define __VIRT_SEVGUEST_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_AUTHTAG_LEN		32
 | 
			
		||||
 | 
			
		||||
/* See SNP spec SNP_GUEST_REQUEST section for the structure */
 | 
			
		||||
enum msg_type {
 | 
			
		||||
	SNP_MSG_TYPE_INVALID = 0,
 | 
			
		||||
	SNP_MSG_CPUID_REQ,
 | 
			
		||||
	SNP_MSG_CPUID_RSP,
 | 
			
		||||
	SNP_MSG_KEY_REQ,
 | 
			
		||||
	SNP_MSG_KEY_RSP,
 | 
			
		||||
	SNP_MSG_REPORT_REQ,
 | 
			
		||||
	SNP_MSG_REPORT_RSP,
 | 
			
		||||
	SNP_MSG_EXPORT_REQ,
 | 
			
		||||
	SNP_MSG_EXPORT_RSP,
 | 
			
		||||
	SNP_MSG_IMPORT_REQ,
 | 
			
		||||
	SNP_MSG_IMPORT_RSP,
 | 
			
		||||
	SNP_MSG_ABSORB_REQ,
 | 
			
		||||
	SNP_MSG_ABSORB_RSP,
 | 
			
		||||
	SNP_MSG_VMRK_REQ,
 | 
			
		||||
	SNP_MSG_VMRK_RSP,
 | 
			
		||||
 | 
			
		||||
	SNP_MSG_TYPE_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum aead_algo {
 | 
			
		||||
	SNP_AEAD_INVALID,
 | 
			
		||||
	SNP_AEAD_AES_256_GCM,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct snp_guest_msg_hdr {
 | 
			
		||||
	u8 authtag[MAX_AUTHTAG_LEN];
 | 
			
		||||
	u64 msg_seqno;
 | 
			
		||||
	u8 rsvd1[8];
 | 
			
		||||
	u8 algo;
 | 
			
		||||
	u8 hdr_version;
 | 
			
		||||
	u16 hdr_sz;
 | 
			
		||||
	u8 msg_type;
 | 
			
		||||
	u8 msg_version;
 | 
			
		||||
	u16 msg_sz;
 | 
			
		||||
	u32 rsvd2;
 | 
			
		||||
	u8 msg_vmpck;
 | 
			
		||||
	u8 rsvd3[35];
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
struct snp_guest_msg {
 | 
			
		||||
	struct snp_guest_msg_hdr hdr;
 | 
			
		||||
	u8 payload[4000];
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The secrets page contains 96-bytes of reserved field that can be used by
 | 
			
		||||
 * the guest OS. The guest OS uses the area to save the message sequence
 | 
			
		||||
 * number for each VMPCK.
 | 
			
		||||
 *
 | 
			
		||||
 * See the GHCB spec section Secret page layout for the format for this area.
 | 
			
		||||
 */
 | 
			
		||||
struct secrets_os_area {
 | 
			
		||||
	u32 msg_seqno_0;
 | 
			
		||||
	u32 msg_seqno_1;
 | 
			
		||||
	u32 msg_seqno_2;
 | 
			
		||||
	u32 msg_seqno_3;
 | 
			
		||||
	u64 ap_jump_table_pa;
 | 
			
		||||
	u8 rsvd[40];
 | 
			
		||||
	u8 guest_usage[32];
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
#define VMPCK_KEY_LEN		32
 | 
			
		||||
 | 
			
		||||
/* See the SNP spec version 0.9 for secrets page format */
 | 
			
		||||
struct snp_secrets_page_layout {
 | 
			
		||||
	u32 version;
 | 
			
		||||
	u32 imien	: 1,
 | 
			
		||||
	    rsvd1	: 31;
 | 
			
		||||
	u32 fms;
 | 
			
		||||
	u32 rsvd2;
 | 
			
		||||
	u8 gosvw[16];
 | 
			
		||||
	u8 vmpck0[VMPCK_KEY_LEN];
 | 
			
		||||
	u8 vmpck1[VMPCK_KEY_LEN];
 | 
			
		||||
	u8 vmpck2[VMPCK_KEY_LEN];
 | 
			
		||||
	u8 vmpck3[VMPCK_KEY_LEN];
 | 
			
		||||
	struct secrets_os_area os_area;
 | 
			
		||||
	u8 rsvd3[3840];
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
#endif /* __VIRT_SEVGUEST_H__ */
 | 
			
		||||
							
								
								
									
										50
									
								
								include/uapi/linux/sev-guest.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								include/uapi/linux/sev-guest.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
 | 
			
		||||
/*
 | 
			
		||||
 * Userspace interface for AMD SEV and SNP guest driver.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 Advanced Micro Devices, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Brijesh Singh <brijesh.singh@amd.com>
 | 
			
		||||
 *
 | 
			
		||||
 * SEV API specification is available at: https://developer.amd.com/sev/
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __UAPI_LINUX_SEV_GUEST_H_
 | 
			
		||||
#define __UAPI_LINUX_SEV_GUEST_H_
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
struct snp_report_req {
 | 
			
		||||
	/* user data that should be included in the report */
 | 
			
		||||
	__u8 user_data[64];
 | 
			
		||||
 | 
			
		||||
	/* The vmpl level to be included in the report */
 | 
			
		||||
	__u32 vmpl;
 | 
			
		||||
 | 
			
		||||
	/* Must be zero filled */
 | 
			
		||||
	__u8 rsvd[28];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct snp_report_resp {
 | 
			
		||||
	/* response data, see SEV-SNP spec for the format */
 | 
			
		||||
	__u8 data[4000];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct snp_guest_request_ioctl {
 | 
			
		||||
	/* message version number (must be non-zero) */
 | 
			
		||||
	__u8 msg_version;
 | 
			
		||||
 | 
			
		||||
	/* Request and response structure address */
 | 
			
		||||
	__u64 req_data;
 | 
			
		||||
	__u64 resp_data;
 | 
			
		||||
 | 
			
		||||
	/* firmware error code on failure (see psp-sev.h) */
 | 
			
		||||
	__u64 fw_err;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SNP_GUEST_REQ_IOC_TYPE	'S'
 | 
			
		||||
 | 
			
		||||
/* Get SNP attestation report */
 | 
			
		||||
#define SNP_GET_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x0, struct snp_guest_request_ioctl)
 | 
			
		||||
 | 
			
		||||
#endif /* __UAPI_LINUX_SEV_GUEST_H_ */
 | 
			
		||||
		Loading…
	
		Reference in a new issue