Merge branch 'work.recursive_removal' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / crypto / ccp / sev-dev.c
index da3872c..3506b20 100644 (file)
@@ -130,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;
        }
 
@@ -142,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;
@@ -151,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);
@@ -195,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;
 }
@@ -214,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;
 
@@ -225,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;
 
@@ -234,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;
 
@@ -296,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;
 }
 
@@ -342,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;
@@ -377,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;
@@ -388,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)
@@ -398,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) {
@@ -419,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;
@@ -436,8 +451,6 @@ cmd:
 
 e_free_blob:
        kfree(blob);
-e_free:
-       kfree(data);
        return ret;
 }
 
@@ -457,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;
 }
@@ -569,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;
 
@@ -579,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);
@@ -600,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) {
@@ -610,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;
@@ -638,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;
@@ -667,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;
                }
@@ -675,7 +677,6 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp)
 
 e_free:
        kfree(id_blob);
-       kfree(data);
 
        return ret;
 }
@@ -725,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;
@@ -743,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 ||
@@ -757,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) {
@@ -783,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;
@@ -816,8 +809,6 @@ e_free_cert:
        kfree(cert_blob);
 e_free_pdh:
        kfree(pdh_blob);
-e_free:
-       kfree(data);
        return ret;
 }
 
@@ -982,6 +973,10 @@ int sev_dev_init(struct psp_device *psp)
        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;
@@ -993,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);
@@ -1008,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;