diff options
Diffstat (limited to 'drivers/crypto/ccp/sev-dev.c')
| -rw-r--r-- | drivers/crypto/ccp/sev-dev.c | 199 | 
1 files changed, 102 insertions, 97 deletions
| diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index cb9b4c4e371e..3506b2050fb8 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -21,6 +21,7 @@  #include <linux/ccp.h>  #include <linux/firmware.h>  #include <linux/gfp.h> +#include <linux/cpufeature.h>  #include <asm/smp.h> @@ -129,6 +130,7 @@ static int sev_cmd_buffer_len(int cmd)  	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);  	default:				return 0;  	} @@ -141,6 +143,7 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)  	struct sev_device *sev;  	unsigned int phys_lsb, phys_msb;  	unsigned int reg, ret = 0; +	int buf_len;  	if (!psp || !psp->sev_data)  		return -ENODEV; @@ -150,15 +153,27 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)  	sev = psp->sev_data; +	buf_len = sev_cmd_buffer_len(cmd); +	if (WARN_ON_ONCE(!data != !buf_len)) +		return -EINVAL; + +	/* +	 * Copy the incoming data to driver's scratch buffer as __pa() will not +	 * work for some memory, e.g. vmalloc'd addresses, and @data may not be +	 * physically contiguous. +	 */ +	if (data) +		memcpy(sev->cmd_buf, data, buf_len); +  	/* Get the physical address of the command buffer */ -	phys_lsb = data ? lower_32_bits(__psp_pa(data)) : 0; -	phys_msb = data ? upper_32_bits(__psp_pa(data)) : 0; +	phys_lsb = data ? lower_32_bits(__psp_pa(sev->cmd_buf)) : 0; +	phys_msb = data ? upper_32_bits(__psp_pa(sev->cmd_buf)) : 0;  	dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n",  		cmd, phys_msb, phys_lsb, psp_timeout);  	print_hex_dump_debug("(in):  ", DUMP_PREFIX_OFFSET, 16, 2, data, -			     sev_cmd_buffer_len(cmd), false); +			     buf_len, false);  	iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg);  	iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg); @@ -194,7 +209,14 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)  	}  	print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data, -			     sev_cmd_buffer_len(cmd), false); +			     buf_len, false); + +	/* +	 * Copy potential output from the PSP back to data.  Do this even on +	 * failure in case the caller wants to glean something from the error. +	 */ +	if (data) +		memcpy(data, sev->cmd_buf, buf_len);  	return ret;  } @@ -213,6 +235,7 @@ static int sev_do_cmd(int cmd, void *data, int *psp_ret)  static int __sev_platform_init_locked(int *error)  {  	struct psp_device *psp = psp_master; +	struct sev_data_init data;  	struct sev_device *sev;  	int rc = 0; @@ -224,6 +247,7 @@ static int __sev_platform_init_locked(int *error)  	if (sev->state == SEV_STATE_INIT)  		return 0; +	memset(&data, 0, sizeof(data));  	if (sev_es_tmr) {  		u64 tmr_pa; @@ -233,12 +257,12 @@ static int __sev_platform_init_locked(int *error)  		 */  		tmr_pa = __pa(sev_es_tmr); -		sev->init_cmd_buf.flags |= SEV_INIT_FLAGS_SEV_ES; -		sev->init_cmd_buf.tmr_address = tmr_pa; -		sev->init_cmd_buf.tmr_len = SEV_ES_TMR_SIZE; +		data.flags |= SEV_INIT_FLAGS_SEV_ES; +		data.tmr_address = tmr_pa; +		data.tmr_len = SEV_ES_TMR_SIZE;  	} -	rc = __sev_do_cmd_locked(SEV_CMD_INIT, &sev->init_cmd_buf, error); +	rc = __sev_do_cmd_locked(SEV_CMD_INIT, &data, error);  	if (rc)  		return rc; @@ -295,15 +319,14 @@ static int sev_platform_shutdown(int *error)  static int sev_get_platform_state(int *state, int *error)  { -	struct sev_device *sev = psp_master->sev_data; +	struct sev_user_data_status data;  	int rc; -	rc = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, -				 &sev->status_cmd_buf, error); +	rc = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, &data, error);  	if (rc)  		return rc; -	*state = sev->status_cmd_buf.state; +	*state = data.state;  	return rc;  } @@ -341,15 +364,14 @@ static int sev_ioctl_do_reset(struct sev_issue_cmd *argp, bool writable)  static int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp)  { -	struct sev_device *sev = psp_master->sev_data; -	struct sev_user_data_status *data = &sev->status_cmd_buf; +	struct sev_user_data_status data;  	int ret; -	ret = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, data, &argp->error); +	ret = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, &data, &argp->error);  	if (ret)  		return ret; -	if (copy_to_user((void __user *)argp->data, data, sizeof(*data))) +	if (copy_to_user((void __user *)argp->data, &data, sizeof(data)))  		ret = -EFAULT;  	return ret; @@ -376,7 +398,7 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable)  {  	struct sev_device *sev = psp_master->sev_data;  	struct sev_user_data_pek_csr input; -	struct sev_data_pek_csr *data; +	struct sev_data_pek_csr data;  	void __user *input_address;  	void *blob = NULL;  	int ret; @@ -387,9 +409,7 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable)  	if (copy_from_user(&input, (void __user *)argp->data, sizeof(input)))  		return -EFAULT; -	data = kzalloc(sizeof(*data), GFP_KERNEL); -	if (!data) -		return -ENOMEM; +	memset(&data, 0, sizeof(data));  	/* userspace wants to query CSR length */  	if (!input.address || !input.length) @@ -397,19 +417,15 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable)  	/* allocate a physically contiguous buffer to store the CSR blob */  	input_address = (void __user *)input.address; -	if (input.length > SEV_FW_BLOB_MAX_SIZE) { -		ret = -EFAULT; -		goto e_free; -	} +	if (input.length > SEV_FW_BLOB_MAX_SIZE) +		return -EFAULT;  	blob = kmalloc(input.length, GFP_KERNEL); -	if (!blob) { -		ret = -ENOMEM; -		goto e_free; -	} +	if (!blob) +		return -ENOMEM; -	data->address = __psp_pa(blob); -	data->len = input.length; +	data.address = __psp_pa(blob); +	data.len = input.length;  cmd:  	if (sev->state == SEV_STATE_UNINIT) { @@ -418,10 +434,10 @@ cmd:  			goto e_free_blob;  	} -	ret = __sev_do_cmd_locked(SEV_CMD_PEK_CSR, data, &argp->error); +	ret = __sev_do_cmd_locked(SEV_CMD_PEK_CSR, &data, &argp->error);  	 /* If we query the CSR length, FW responded with expected data. */ -	input.length = data->len; +	input.length = data.len;  	if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) {  		ret = -EFAULT; @@ -435,8 +451,6 @@ cmd:  e_free_blob:  	kfree(blob); -e_free: -	kfree(data);  	return ret;  } @@ -456,21 +470,20 @@ EXPORT_SYMBOL_GPL(psp_copy_user_blob);  static int sev_get_api_version(void)  {  	struct sev_device *sev = psp_master->sev_data; -	struct sev_user_data_status *status; +	struct sev_user_data_status status;  	int error = 0, ret; -	status = &sev->status_cmd_buf; -	ret = sev_platform_status(status, &error); +	ret = sev_platform_status(&status, &error);  	if (ret) {  		dev_err(sev->dev,  			"SEV: failed to get status. Error: %#x\n", error);  		return 1;  	} -	sev->api_major = status->api_major; -	sev->api_minor = status->api_minor; -	sev->build = status->build; -	sev->state = status->state; +	sev->api_major = status.api_major; +	sev->api_minor = status.api_minor; +	sev->build = status.build; +	sev->state = status.state;  	return 0;  } @@ -568,7 +581,7 @@ static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)  {  	struct sev_device *sev = psp_master->sev_data;  	struct sev_user_data_pek_cert_import input; -	struct sev_data_pek_cert_import *data; +	struct sev_data_pek_cert_import data;  	void *pek_blob, *oca_blob;  	int ret; @@ -578,19 +591,14 @@ static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)  	if (copy_from_user(&input, (void __user *)argp->data, sizeof(input)))  		return -EFAULT; -	data = kzalloc(sizeof(*data), GFP_KERNEL); -	if (!data) -		return -ENOMEM; -  	/* copy PEK certificate blobs from userspace */  	pek_blob = psp_copy_user_blob(input.pek_cert_address, input.pek_cert_len); -	if (IS_ERR(pek_blob)) { -		ret = PTR_ERR(pek_blob); -		goto e_free; -	} +	if (IS_ERR(pek_blob)) +		return PTR_ERR(pek_blob); -	data->pek_cert_address = __psp_pa(pek_blob); -	data->pek_cert_len = input.pek_cert_len; +	data.reserved = 0; +	data.pek_cert_address = __psp_pa(pek_blob); +	data.pek_cert_len = input.pek_cert_len;  	/* copy PEK certificate blobs from userspace */  	oca_blob = psp_copy_user_blob(input.oca_cert_address, input.oca_cert_len); @@ -599,8 +607,8 @@ static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)  		goto e_free_pek;  	} -	data->oca_cert_address = __psp_pa(oca_blob); -	data->oca_cert_len = input.oca_cert_len; +	data.oca_cert_address = __psp_pa(oca_blob); +	data.oca_cert_len = input.oca_cert_len;  	/* If platform is not in INIT state then transition it to INIT */  	if (sev->state != SEV_STATE_INIT) { @@ -609,21 +617,19 @@ static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)  			goto e_free_oca;  	} -	ret = __sev_do_cmd_locked(SEV_CMD_PEK_CERT_IMPORT, data, &argp->error); +	ret = __sev_do_cmd_locked(SEV_CMD_PEK_CERT_IMPORT, &data, &argp->error);  e_free_oca:  	kfree(oca_blob);  e_free_pek:  	kfree(pek_blob); -e_free: -	kfree(data);  	return ret;  }  static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp)  {  	struct sev_user_data_get_id2 input; -	struct sev_data_get_id *data; +	struct sev_data_get_id data;  	void __user *input_address;  	void *id_blob = NULL;  	int ret; @@ -637,28 +643,25 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp)  	input_address = (void __user *)input.address; -	data = kzalloc(sizeof(*data), GFP_KERNEL); -	if (!data) -		return -ENOMEM; -  	if (input.address && input.length) {  		id_blob = kmalloc(input.length, GFP_KERNEL); -		if (!id_blob) { -			kfree(data); +		if (!id_blob)  			return -ENOMEM; -		} -		data->address = __psp_pa(id_blob); -		data->len = input.length; +		data.address = __psp_pa(id_blob); +		data.len = input.length; +	} else { +		data.address = 0; +		data.len = 0;  	} -	ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, data, &argp->error); +	ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, &data, &argp->error);  	/*  	 * Firmware will return the length of the ID value (either the minimum  	 * required length or the actual length written), return it to the user.  	 */ -	input.length = data->len; +	input.length = data.len;  	if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) {  		ret = -EFAULT; @@ -666,7 +669,7 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp)  	}  	if (id_blob) { -		if (copy_to_user(input_address, id_blob, data->len)) { +		if (copy_to_user(input_address, id_blob, data.len)) {  			ret = -EFAULT;  			goto e_free;  		} @@ -674,7 +677,6 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp)  e_free:  	kfree(id_blob); -	kfree(data);  	return ret;  } @@ -724,7 +726,7 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable)  	struct sev_device *sev = psp_master->sev_data;  	struct sev_user_data_pdh_cert_export input;  	void *pdh_blob = NULL, *cert_blob = NULL; -	struct sev_data_pdh_cert_export *data; +	struct sev_data_pdh_cert_export data;  	void __user *input_cert_chain_address;  	void __user *input_pdh_cert_address;  	int ret; @@ -742,9 +744,7 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable)  	if (copy_from_user(&input, (void __user *)argp->data, sizeof(input)))  		return -EFAULT; -	data = kzalloc(sizeof(*data), GFP_KERNEL); -	if (!data) -		return -ENOMEM; +	memset(&data, 0, sizeof(data));  	/* Userspace wants to query the certificate length. */  	if (!input.pdh_cert_address || @@ -756,25 +756,19 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable)  	input_cert_chain_address = (void __user *)input.cert_chain_address;  	/* Allocate a physically contiguous buffer to store the PDH blob. */ -	if (input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) { -		ret = -EFAULT; -		goto e_free; -	} +	if (input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) +		return -EFAULT;  	/* Allocate a physically contiguous buffer to store the cert chain blob. */ -	if (input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) { -		ret = -EFAULT; -		goto e_free; -	} +	if (input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) +		return -EFAULT;  	pdh_blob = kmalloc(input.pdh_cert_len, GFP_KERNEL); -	if (!pdh_blob) { -		ret = -ENOMEM; -		goto e_free; -	} +	if (!pdh_blob) +		return -ENOMEM; -	data->pdh_cert_address = __psp_pa(pdh_blob); -	data->pdh_cert_len = input.pdh_cert_len; +	data.pdh_cert_address = __psp_pa(pdh_blob); +	data.pdh_cert_len = input.pdh_cert_len;  	cert_blob = kmalloc(input.cert_chain_len, GFP_KERNEL);  	if (!cert_blob) { @@ -782,15 +776,15 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable)  		goto e_free_pdh;  	} -	data->cert_chain_address = __psp_pa(cert_blob); -	data->cert_chain_len = input.cert_chain_len; +	data.cert_chain_address = __psp_pa(cert_blob); +	data.cert_chain_len = input.cert_chain_len;  cmd: -	ret = __sev_do_cmd_locked(SEV_CMD_PDH_CERT_EXPORT, data, &argp->error); +	ret = __sev_do_cmd_locked(SEV_CMD_PDH_CERT_EXPORT, &data, &argp->error);  	/* If we query the length, FW responded with expected data. */ -	input.cert_chain_len = data->cert_chain_len; -	input.pdh_cert_len = data->pdh_cert_len; +	input.cert_chain_len = data.cert_chain_len; +	input.pdh_cert_len = data.pdh_cert_len;  	if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) {  		ret = -EFAULT; @@ -815,8 +809,6 @@ e_free_cert:  	kfree(cert_blob);  e_free_pdh:  	kfree(pdh_blob); -e_free: -	kfree(data);  	return ret;  } @@ -972,10 +964,19 @@ int sev_dev_init(struct psp_device *psp)  	struct sev_device *sev;  	int ret = -ENOMEM; +	if (!boot_cpu_has(X86_FEATURE_SEV)) { +		dev_info_once(dev, "SEV: memory encryption not enabled by BIOS\n"); +		return 0; +	} +  	sev = devm_kzalloc(dev, sizeof(*sev), GFP_KERNEL);  	if (!sev)  		goto e_err; +	sev->cmd_buf = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); +	if (!sev->cmd_buf) +		goto e_sev; +  	psp->sev_data = sev;  	sev->dev = dev; @@ -987,7 +988,7 @@ int sev_dev_init(struct psp_device *psp)  	if (!sev->vdata) {  		ret = -ENODEV;  		dev_err(dev, "sev: missing driver data\n"); -		goto e_err; +		goto e_buf;  	}  	psp_set_sev_irq_handler(psp, sev_irq_handler, sev); @@ -1002,6 +1003,10 @@ int sev_dev_init(struct psp_device *psp)  e_irq:  	psp_clear_sev_irq_handler(psp); +e_buf: +	devm_free_pages(dev, (unsigned long)sev->cmd_buf); +e_sev: +	devm_kfree(dev, sev);  e_err:  	psp->sev_data = NULL; | 
