3168 lines · 79.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * AMD Secure Encrypted Virtualization (SEV) interface * * Copyright (C) 2016,2019 Advanced Micro Devices, Inc. * * Author: Brijesh Singh <brijesh.singh@amd.com> */ #include <linux/bitfield.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/kthread.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/spinlock_types.h>#include <linux/types.h>#include <linux/mutex.h>#include <linux/delay.h>#include <linux/hw_random.h>#include <linux/ccp.h>#include <linux/firmware.h>#include <linux/panic_notifier.h>#include <linux/gfp.h>#include <linux/cpufeature.h>#include <linux/fs.h>#include <linux/fs_struct.h>#include <linux/psp.h>#include <linux/amd-iommu.h>#include <linux/crash_dump.h> #include <asm/smp.h>#include <asm/cacheflush.h>#include <asm/e820/types.h>#include <asm/sev.h>#include <asm/msr.h> #include "psp-dev.h"#include "sev-dev.h" #define DEVICE_NAME		"sev"#define SEV_FW_FILE		"amd/sev.fw"#define SEV_FW_NAME_SIZE	64 /* Minimum firmware version required for the SEV-SNP support */#define SNP_MIN_API_MAJOR	1#define SNP_MIN_API_MINOR	51 /* * Maximum number of firmware-writable buffers that might be specified * in the parameters of a legacy SEV command buffer. */#define CMD_BUF_FW_WRITABLE_MAX 2 /* Leave room in the descriptor array for an end-of-list indicator. */#define CMD_BUF_DESC_MAX (CMD_BUF_FW_WRITABLE_MAX + 1) static DEFINE_MUTEX(sev_cmd_mutex);static struct sev_misc_dev *misc_dev; static int psp_cmd_timeout = 100;module_param(psp_cmd_timeout, int, 0644);MODULE_PARM_DESC(psp_cmd_timeout, " default timeout value, in seconds, for PSP commands"); static int psp_probe_timeout = 5;module_param(psp_probe_timeout, int, 0644);MODULE_PARM_DESC(psp_probe_timeout, " default timeout value, in seconds, during PSP device probe"); static char *init_ex_path;module_param(init_ex_path, charp, 0444);MODULE_PARM_DESC(init_ex_path, " Path for INIT_EX data; if set try INIT_EX"); static bool psp_init_on_probe = true;module_param(psp_init_on_probe, bool, 0444);MODULE_PARM_DESC(psp_init_on_probe, "  if true, the PSP will be initialized on module init. Else the PSP will be initialized on the first command requiring it"); #if IS_ENABLED(CONFIG_PCI_TSM)static bool sev_tio_enabled = true;module_param_named(tio, sev_tio_enabled, bool, 0444);MODULE_PARM_DESC(tio, "Enables TIO in SNP_INIT_EX");#elsestatic const bool sev_tio_enabled = false;#endif MODULE_FIRMWARE("amd/amd_sev_fam17h_model0xh.sbin"); /* 1st gen EPYC */MODULE_FIRMWARE("amd/amd_sev_fam17h_model3xh.sbin"); /* 2nd gen EPYC */MODULE_FIRMWARE("amd/amd_sev_fam19h_model0xh.sbin"); /* 3rd gen EPYC */MODULE_FIRMWARE("amd/amd_sev_fam19h_model1xh.sbin"); /* 4th gen EPYC */ static bool psp_dead;static int psp_timeout; enum snp_hv_fixed_pages_state {	ALLOCATED,	HV_FIXED,}; struct snp_hv_fixed_pages_entry {	struct list_head list;	struct page *page;	unsigned int order;	bool free;	enum snp_hv_fixed_pages_state page_state;}; static LIST_HEAD(snp_hv_fixed_pages); /* Trusted Memory Region (TMR): *   The TMR is a 1MB area that must be 1MB aligned.  Use the page allocator *   to allocate the memory, which will return aligned memory for the specified *   allocation order. * * When SEV-SNP is enabled the TMR needs to be 2MB aligned and 2MB sized. */#define SEV_TMR_SIZE		(1024 * 1024)#define SNP_TMR_SIZE		(2 * 1024 * 1024) static void *sev_es_tmr;static size_t sev_es_tmr_size = SEV_TMR_SIZE; /* INIT_EX NV Storage: *   The NV Storage is a 32Kb area and must be 4Kb page aligned.  Use the page *   allocator to allocate the memory, which will return aligned memory for the *   specified allocation order. */#define NV_LENGTH (32 * 1024)static void *sev_init_ex_buffer; static void __sev_firmware_shutdown(struct sev_device *sev, bool panic); static int snp_shutdown_on_panic(struct notifier_block *nb,				 unsigned long reason, void *arg); static struct notifier_block snp_panic_notifier = {	.notifier_call = snp_shutdown_on_panic,}; static inline bool sev_version_greater_or_equal(u8 maj, u8 min){	struct sev_device *sev = psp_master->sev_data; 	if (sev->api_major > maj)		return true; 	if (sev->api_major == maj && sev->api_minor >= min)		return true; 	return false;} static void sev_irq_handler(int irq, void *data, unsigned int status){	struct sev_device *sev = data;	int reg; 	/* Check if it is command completion: */	if (!(status & SEV_CMD_COMPLETE))		return; 	/* Check if it is SEV command completion: */	reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg);	if (FIELD_GET(PSP_CMDRESP_RESP, reg)) {		sev->int_rcvd = 1;		wake_up(&sev->int_queue);	}} static int sev_wait_cmd_ioc(struct sev_device *sev,			    unsigned int *reg, unsigned int timeout){	int ret; 	/*	 * If invoked during panic handling, local interrupts are disabled,	 * so the PSP command completion interrupt can't be used. Poll for	 * PSP command completion instead.	 */	if (irqs_disabled()) {		unsigned long timeout_usecs = (timeout * USEC_PER_SEC) / 10; 		/* Poll for SEV command completion: */		while (timeout_usecs--) {			*reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg);			if (*reg & PSP_CMDRESP_RESP)				return 0; 			udelay(10);		}		return -ETIMEDOUT;	} 	ret = wait_event_timeout(sev->int_queue,			sev->int_rcvd, timeout * HZ);	if (!ret)		return -ETIMEDOUT; 	*reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); 	return 0;} static int sev_cmd_buffer_len(int cmd){	switch (cmd) {	case SEV_CMD_INIT:			return sizeof(struct sev_data_init);	case SEV_CMD_INIT_EX:                   return sizeof(struct sev_data_init_ex);	case SEV_CMD_SNP_SHUTDOWN_EX:		return sizeof(struct sev_data_snp_shutdown_ex);	case SEV_CMD_SNP_INIT_EX:		return sizeof(struct sev_data_snp_init_ex);	case SEV_CMD_PLATFORM_STATUS:		return sizeof(struct sev_user_data_status);	case SEV_CMD_PEK_CSR:			return sizeof(struct sev_data_pek_csr);	case SEV_CMD_PEK_CERT_IMPORT:		return sizeof(struct sev_data_pek_cert_import);	case SEV_CMD_PDH_CERT_EXPORT:		return sizeof(struct sev_data_pdh_cert_export);	case SEV_CMD_LAUNCH_START:		return sizeof(struct sev_data_launch_start);	case SEV_CMD_LAUNCH_UPDATE_DATA:	return sizeof(struct sev_data_launch_update_data);	case SEV_CMD_LAUNCH_UPDATE_VMSA:	return sizeof(struct sev_data_launch_update_vmsa);	case SEV_CMD_LAUNCH_FINISH:		return sizeof(struct sev_data_launch_finish);	case SEV_CMD_LAUNCH_MEASURE:		return sizeof(struct sev_data_launch_measure);	case SEV_CMD_ACTIVATE:			return sizeof(struct sev_data_activate);	case SEV_CMD_DEACTIVATE:		return sizeof(struct sev_data_deactivate);	case SEV_CMD_DECOMMISSION:		return sizeof(struct sev_data_decommission);	case SEV_CMD_GUEST_STATUS:		return sizeof(struct sev_data_guest_status);	case SEV_CMD_DBG_DECRYPT:		return sizeof(struct sev_data_dbg);	case SEV_CMD_DBG_ENCRYPT:		return sizeof(struct sev_data_dbg);	case SEV_CMD_SEND_START:		return sizeof(struct sev_data_send_start);	case SEV_CMD_SEND_UPDATE_DATA:		return sizeof(struct sev_data_send_update_data);	case SEV_CMD_SEND_UPDATE_VMSA:		return sizeof(struct sev_data_send_update_vmsa);	case SEV_CMD_SEND_FINISH:		return sizeof(struct sev_data_send_finish);	case SEV_CMD_RECEIVE_START:		return sizeof(struct sev_data_receive_start);	case SEV_CMD_RECEIVE_FINISH:		return sizeof(struct sev_data_receive_finish);	case SEV_CMD_RECEIVE_UPDATE_DATA:	return sizeof(struct sev_data_receive_update_data);	case SEV_CMD_RECEIVE_UPDATE_VMSA:	return sizeof(struct sev_data_receive_update_vmsa);	case SEV_CMD_LAUNCH_UPDATE_SECRET:	return sizeof(struct sev_data_launch_secret);	case SEV_CMD_DOWNLOAD_FIRMWARE:		return sizeof(struct sev_data_download_firmware);	case SEV_CMD_GET_ID:			return sizeof(struct sev_data_get_id);	case SEV_CMD_ATTESTATION_REPORT:	return sizeof(struct sev_data_attestation_report);	case SEV_CMD_SEND_CANCEL:		return sizeof(struct sev_data_send_cancel);	case SEV_CMD_SNP_GCTX_CREATE:		return sizeof(struct sev_data_snp_addr);	case SEV_CMD_SNP_LAUNCH_START:		return sizeof(struct sev_data_snp_launch_start);	case SEV_CMD_SNP_LAUNCH_UPDATE:		return sizeof(struct sev_data_snp_launch_update);	case SEV_CMD_SNP_ACTIVATE:		return sizeof(struct sev_data_snp_activate);	case SEV_CMD_SNP_DECOMMISSION:		return sizeof(struct sev_data_snp_addr);	case SEV_CMD_SNP_PAGE_RECLAIM:		return sizeof(struct sev_data_snp_page_reclaim);	case SEV_CMD_SNP_GUEST_STATUS:		return sizeof(struct sev_data_snp_guest_status);	case SEV_CMD_SNP_LAUNCH_FINISH:		return sizeof(struct sev_data_snp_launch_finish);	case SEV_CMD_SNP_DBG_DECRYPT:		return sizeof(struct sev_data_snp_dbg);	case SEV_CMD_SNP_DBG_ENCRYPT:		return sizeof(struct sev_data_snp_dbg);	case SEV_CMD_SNP_VERIFY_MITIGATION:	return sizeof(struct sev_data_snp_verify_mitigation);	case SEV_CMD_SNP_PAGE_UNSMASH:		return sizeof(struct sev_data_snp_page_unsmash);	case SEV_CMD_SNP_PLATFORM_STATUS:	return sizeof(struct sev_data_snp_addr);	case SEV_CMD_SNP_GUEST_REQUEST:		return sizeof(struct sev_data_snp_guest_request);	case SEV_CMD_SNP_CONFIG:		return sizeof(struct sev_user_data_snp_config);	case SEV_CMD_SNP_COMMIT:		return sizeof(struct sev_data_snp_commit);	case SEV_CMD_SNP_FEATURE_INFO:		return sizeof(struct sev_data_snp_feature_info);	case SEV_CMD_SNP_VLEK_LOAD:		return sizeof(struct sev_user_data_snp_vlek_load);	default:				return sev_tio_cmd_buffer_len(cmd);	} 	return 0;} static struct file *open_file_as_root(const char *filename, int flags, umode_t mode){	CLASS(prepare_creds, cred)();	if (!cred)		return ERR_PTR(-ENOMEM); 	cred->fsuid = GLOBAL_ROOT_UID; 	scoped_with_init_fs() {		scoped_with_creds(cred)			return filp_open(filename, flags, mode);	}} static int sev_read_init_ex_file(void){	struct sev_device *sev = psp_master->sev_data;	struct file *fp;	ssize_t nread; 	lockdep_assert_held(&sev_cmd_mutex); 	if (!sev_init_ex_buffer)		return -EOPNOTSUPP; 	fp = open_file_as_root(init_ex_path, O_RDONLY, 0);	if (IS_ERR(fp)) {		int ret = PTR_ERR(fp); 		if (ret == -ENOENT) {			dev_info(sev->dev,				"SEV: %s does not exist and will be created later.\n",				init_ex_path);			ret = 0;		} else {			dev_err(sev->dev,				"SEV: could not open %s for read, error %d\n",				init_ex_path, ret);		}		return ret;	} 	nread = kernel_read(fp, sev_init_ex_buffer, NV_LENGTH, NULL);	if (nread != NV_LENGTH) {		dev_info(sev->dev,			"SEV: could not read %u bytes to non volatile memory area, ret %ld\n",			NV_LENGTH, nread);	} 	dev_dbg(sev->dev, "SEV: read %ld bytes from NV file\n", nread);	filp_close(fp, NULL); 	return 0;} static int sev_write_init_ex_file(void){	struct sev_device *sev = psp_master->sev_data;	struct file *fp;	loff_t offset = 0;	ssize_t nwrite; 	lockdep_assert_held(&sev_cmd_mutex); 	if (!sev_init_ex_buffer)		return 0; 	fp = open_file_as_root(init_ex_path, O_CREAT | O_WRONLY, 0600);	if (IS_ERR(fp)) {		int ret = PTR_ERR(fp); 		dev_err(sev->dev,			"SEV: could not open file for write, error %d\n",			ret);		return ret;	} 	nwrite = kernel_write(fp, sev_init_ex_buffer, NV_LENGTH, &offset);	vfs_fsync(fp, 0);	filp_close(fp, NULL); 	if (nwrite != NV_LENGTH) {		dev_err(sev->dev,			"SEV: failed to write %u bytes to non volatile memory area, ret %ld\n",			NV_LENGTH, nwrite);		return -EIO;	} 	dev_dbg(sev->dev, "SEV: write successful to NV file\n"); 	return 0;} static int sev_write_init_ex_file_if_required(int cmd_id){	lockdep_assert_held(&sev_cmd_mutex); 	if (!sev_init_ex_buffer)		return 0; 	/*	 * Only a few platform commands modify the SPI/NV area, but none of the	 * non-platform commands do. Only INIT(_EX), PLATFORM_RESET, PEK_GEN,	 * PEK_CERT_IMPORT, and PDH_GEN do.	 */	switch (cmd_id) {	case SEV_CMD_FACTORY_RESET:	case SEV_CMD_INIT_EX:	case SEV_CMD_PDH_GEN:	case SEV_CMD_PEK_CERT_IMPORT:	case SEV_CMD_PEK_GEN:		break;	default:		return 0;	} 	return sev_write_init_ex_file();} int snp_reclaim_pages(unsigned long paddr, unsigned int npages, bool locked){	int ret, err, i; 	paddr = __sme_clr(ALIGN_DOWN(paddr, PAGE_SIZE)); 	for (i = 0; i < npages; i++, paddr += PAGE_SIZE) {		struct sev_data_snp_page_reclaim data = {0}; 		data.paddr = paddr; 		if (locked)			ret = __sev_do_cmd_locked(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err);		else			ret = sev_do_cmd(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err); 		if (ret)			goto cleanup; 		ret = rmp_make_shared(__phys_to_pfn(paddr), PG_LEVEL_4K);		if (ret)			goto cleanup;	} 	return 0; cleanup:	/*	 * If there was a failure reclaiming the page then it is no longer safe	 * to release it back to the system; leak it instead.	 */	snp_leak_pages(__phys_to_pfn(paddr), npages - i);	return ret;}EXPORT_SYMBOL_GPL(snp_reclaim_pages); static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, bool locked){	unsigned long pfn = __sme_clr(paddr) >> PAGE_SHIFT;	int rc, i; 	for (i = 0; i < npages; i++, pfn++) {		rc = rmp_make_private(pfn, 0, PG_LEVEL_4K, 0, true);		if (rc)			goto cleanup;	} 	return 0; cleanup:	/*	 * Try unrolling the firmware state changes by	 * reclaiming the pages which were already changed to the	 * firmware state.	 */	snp_reclaim_pages(paddr, i, locked); 	return rc;} static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order, bool locked){	unsigned long npages = 1ul << order, paddr;	struct sev_device *sev;	struct page *page; 	if (!psp_master || !psp_master->sev_data)		return NULL; 	page = alloc_pages(gfp_mask, order);	if (!page)		return NULL; 	/* If SEV-SNP is initialized then add the page in RMP table. */	sev = psp_master->sev_data;	if (!sev->snp_initialized)		return page; 	paddr = __pa((unsigned long)page_address(page));	if (rmp_mark_pages_firmware(paddr, npages, locked))		return NULL; 	return page;} void *snp_alloc_firmware_page(gfp_t gfp_mask){	struct page *page; 	page = __snp_alloc_firmware_pages(gfp_mask, 0, false); 	return page ? page_address(page) : NULL;}EXPORT_SYMBOL_GPL(snp_alloc_firmware_page); static void __snp_free_firmware_pages(struct page *page, int order, bool locked){	struct sev_device *sev = psp_master->sev_data;	unsigned long paddr, npages = 1ul << order; 	if (!page)		return; 	paddr = __pa((unsigned long)page_address(page));	if (sev->snp_initialized &&	    snp_reclaim_pages(paddr, npages, locked))		return; 	__free_pages(page, order);} void snp_free_firmware_page(void *addr){	if (!addr)		return; 	__snp_free_firmware_pages(virt_to_page(addr), 0, false);}EXPORT_SYMBOL_GPL(snp_free_firmware_page); 

Lines 1–500 of 3168.