crypto: ccp - add timeout support in the SEV command
authorBrijesh Singh <brijesh.singh@amd.com>
Wed, 15 Aug 2018 21:11:25 +0000 (16:11 -0500)
committerHerbert Xu <herbert@gondor.apana.org.au>
Thu, 13 Sep 2018 05:27:43 +0000 (13:27 +0800)
Currently, the CCP driver assumes that the SEV command issued to the PSP
will always return (i.e. it will never hang).  But recently, firmware bugs
have shown that a command can hang.  Since of the SEV commands are used
in probe routines, this can cause boot hangs and/or loss of virtualization
capabilities.

To protect against firmware bugs, add a timeout in the SEV command
execution flow.  If a command does not complete within the specified
timeout then return -ETIMEOUT and stop the driver from executing any
further commands since the state of the SEV firmware is unknown.

Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gary Hook <Gary.Hook@amd.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
drivers/crypto/ccp/psp-dev.c

index 218739b961fe37b8c633443173d3026e19df0a69..72790d88236d4d73b56c5f1d26efcbfadc033c6b 100644 (file)
@@ -38,6 +38,17 @@ static DEFINE_MUTEX(sev_cmd_mutex);
 static struct sev_misc_dev *misc_dev;
 static struct psp_device *psp_master;
 
+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 bool psp_dead;
+static int psp_timeout;
+
 static struct psp_device *psp_alloc_struct(struct sp_device *sp)
 {
        struct device *dev = sp->dev;
@@ -82,10 +93,19 @@ done:
        return IRQ_HANDLED;
 }
 
-static void sev_wait_cmd_ioc(struct psp_device *psp, unsigned int *reg)
+static int sev_wait_cmd_ioc(struct psp_device *psp,
+                           unsigned int *reg, unsigned int timeout)
 {
-       wait_event(psp->sev_int_queue, psp->sev_int_rcvd);
+       int ret;
+
+       ret = wait_event_timeout(psp->sev_int_queue,
+                       psp->sev_int_rcvd, timeout * HZ);
+       if (!ret)
+               return -ETIMEDOUT;
+
        *reg = ioread32(psp->io_regs + psp->vdata->cmdresp_reg);
+
+       return 0;
 }
 
 static int sev_cmd_buffer_len(int cmd)
@@ -133,12 +153,15 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
        if (!psp)
                return -ENODEV;
 
+       if (psp_dead)
+               return -EBUSY;
+
        /* 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;
 
-       dev_dbg(psp->dev, "sev command id %#x buffer 0x%08x%08x\n",
-               cmd, phys_msb, phys_lsb);
+       dev_dbg(psp->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);
@@ -154,7 +177,18 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
        iowrite32(reg, psp->io_regs + psp->vdata->cmdresp_reg);
 
        /* wait for command completion */
-       sev_wait_cmd_ioc(psp, &reg);
+       ret = sev_wait_cmd_ioc(psp, &reg, psp_timeout);
+       if (ret) {
+               if (psp_ret)
+                       *psp_ret = 0;
+
+               dev_err(psp->dev, "sev command %#x timed out, disabling PSP \n", cmd);
+               psp_dead = true;
+
+               return ret;
+       }
+
+       psp_timeout = psp_cmd_timeout;
 
        if (psp_ret)
                *psp_ret = reg & PSP_CMDRESP_ERR_MASK;
@@ -888,6 +922,8 @@ void psp_pci_init(void)
 
        psp_master = sp->psp_data;
 
+       psp_timeout = psp_probe_timeout;
+
        if (sev_get_api_version())
                goto err;