crypto: ccp - create a generic psp-dev file
The PSP (Platform Security Processor) provides support for key management commands in Secure Encrypted Virtualization (SEV) mode, along with software-based Trusted Execution Environment (TEE) to enable third-party Trusted Applications. Therefore, introduce psp-dev.c and psp-dev.h files, which can invoke SEV (or TEE) initialization based on platform feature support. TEE interface support will be introduced in a later patch. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Jens Wiklander <jens.wiklander@linaro.org> Co-developed-by: Devaraj Rangasamy <Devaraj.Rangasamy@amd.com> Signed-off-by: Devaraj Rangasamy <Devaraj.Rangasamy@amd.com> Signed-off-by: Rijo Thomas <Rijo-john.Thomas@amd.com> Acked-by: Gary R Hook <gary.hook@amd.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
9b67d08dbc
commit
b93566f1bb
|
@ -8,7 +8,8 @@ ccp-$(CONFIG_CRYPTO_DEV_SP_CCP) += ccp-dev.o \
|
|||
ccp-dmaengine.o
|
||||
ccp-$(CONFIG_CRYPTO_DEV_CCP_DEBUGFS) += ccp-debugfs.o
|
||||
ccp-$(CONFIG_PCI) += sp-pci.o
|
||||
ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += sev-dev.o
|
||||
ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o \
|
||||
sev-dev.o
|
||||
|
||||
obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o
|
||||
ccp-crypto-objs := ccp-crypto-main.o \
|
||||
|
|
194
drivers/crypto/ccp/psp-dev.c
Normal file
194
drivers/crypto/ccp/psp-dev.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* AMD Platform Security Processor (PSP) interface
|
||||
*
|
||||
* Copyright (C) 2016,2019 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Brijesh Singh <brijesh.singh@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
#include "sp-dev.h"
|
||||
#include "psp-dev.h"
|
||||
#include "sev-dev.h"
|
||||
|
||||
struct psp_device *psp_master;
|
||||
|
||||
static struct psp_device *psp_alloc_struct(struct sp_device *sp)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct psp_device *psp;
|
||||
|
||||
psp = devm_kzalloc(dev, sizeof(*psp), GFP_KERNEL);
|
||||
if (!psp)
|
||||
return NULL;
|
||||
|
||||
psp->dev = dev;
|
||||
psp->sp = sp;
|
||||
|
||||
snprintf(psp->name, sizeof(psp->name), "psp-%u", sp->ord);
|
||||
|
||||
return psp;
|
||||
}
|
||||
|
||||
static irqreturn_t psp_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct psp_device *psp = data;
|
||||
unsigned int status;
|
||||
|
||||
/* Read the interrupt status: */
|
||||
status = ioread32(psp->io_regs + psp->vdata->intsts_reg);
|
||||
|
||||
/* invoke subdevice interrupt handlers */
|
||||
if (status) {
|
||||
if (psp->sev_irq_handler)
|
||||
psp->sev_irq_handler(irq, psp->sev_irq_data, status);
|
||||
}
|
||||
|
||||
/* Clear the interrupt status by writing the same value we read. */
|
||||
iowrite32(status, psp->io_regs + psp->vdata->intsts_reg);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int psp_check_sev_support(struct psp_device *psp)
|
||||
{
|
||||
unsigned int val = ioread32(psp->io_regs + psp->vdata->feature_reg);
|
||||
|
||||
/*
|
||||
* Check for a access to the registers. If this read returns
|
||||
* 0xffffffff, it's likely that the system is running a broken
|
||||
* BIOS which disallows access to the device. Stop here and
|
||||
* fail the PSP initialization (but not the load, as the CCP
|
||||
* could get properly initialized).
|
||||
*/
|
||||
if (val == 0xffffffff) {
|
||||
dev_notice(psp->dev, "psp: unable to access the device: you might be running a broken BIOS.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(val & 1)) {
|
||||
/* Device does not support the SEV feature */
|
||||
dev_dbg(psp->dev, "psp does not support SEV\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int psp_dev_init(struct sp_device *sp)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct psp_device *psp;
|
||||
int ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
psp = psp_alloc_struct(sp);
|
||||
if (!psp)
|
||||
goto e_err;
|
||||
|
||||
sp->psp_data = psp;
|
||||
|
||||
psp->vdata = (struct psp_vdata *)sp->dev_vdata->psp_vdata;
|
||||
if (!psp->vdata) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "missing driver data\n");
|
||||
goto e_err;
|
||||
}
|
||||
|
||||
psp->io_regs = sp->io_map;
|
||||
|
||||
ret = psp_check_sev_support(psp);
|
||||
if (ret)
|
||||
goto e_disable;
|
||||
|
||||
/* Disable and clear interrupts until ready */
|
||||
iowrite32(0, psp->io_regs + psp->vdata->inten_reg);
|
||||
iowrite32(-1, psp->io_regs + psp->vdata->intsts_reg);
|
||||
|
||||
/* Request an irq */
|
||||
ret = sp_request_psp_irq(psp->sp, psp_irq_handler, psp->name, psp);
|
||||
if (ret) {
|
||||
dev_err(dev, "psp: unable to allocate an IRQ\n");
|
||||
goto e_err;
|
||||
}
|
||||
|
||||
ret = sev_dev_init(psp);
|
||||
if (ret)
|
||||
goto e_irq;
|
||||
|
||||
if (sp->set_psp_master_device)
|
||||
sp->set_psp_master_device(sp);
|
||||
|
||||
/* Enable interrupt */
|
||||
iowrite32(-1, psp->io_regs + psp->vdata->inten_reg);
|
||||
|
||||
dev_notice(dev, "psp enabled\n");
|
||||
|
||||
return 0;
|
||||
|
||||
e_irq:
|
||||
sp_free_psp_irq(psp->sp, psp);
|
||||
e_err:
|
||||
sp->psp_data = NULL;
|
||||
|
||||
dev_notice(dev, "psp initialization failed\n");
|
||||
|
||||
return ret;
|
||||
|
||||
e_disable:
|
||||
sp->psp_data = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void psp_dev_destroy(struct sp_device *sp)
|
||||
{
|
||||
struct psp_device *psp = sp->psp_data;
|
||||
|
||||
if (!psp)
|
||||
return;
|
||||
|
||||
sev_dev_destroy(psp);
|
||||
|
||||
sp_free_psp_irq(sp, psp);
|
||||
}
|
||||
|
||||
void psp_set_sev_irq_handler(struct psp_device *psp, psp_irq_handler_t handler,
|
||||
void *data)
|
||||
{
|
||||
psp->sev_irq_data = data;
|
||||
psp->sev_irq_handler = handler;
|
||||
}
|
||||
|
||||
void psp_clear_sev_irq_handler(struct psp_device *psp)
|
||||
{
|
||||
psp_set_sev_irq_handler(psp, NULL, NULL);
|
||||
}
|
||||
|
||||
struct psp_device *psp_get_master_device(void)
|
||||
{
|
||||
struct sp_device *sp = sp_get_psp_master_device();
|
||||
|
||||
return sp ? sp->psp_data : NULL;
|
||||
}
|
||||
|
||||
void psp_pci_init(void)
|
||||
{
|
||||
psp_master = psp_get_master_device();
|
||||
|
||||
if (!psp_master)
|
||||
return;
|
||||
|
||||
sev_pci_init();
|
||||
}
|
||||
|
||||
void psp_pci_exit(void)
|
||||
{
|
||||
if (!psp_master)
|
||||
return;
|
||||
|
||||
sev_pci_exit();
|
||||
}
|
52
drivers/crypto/ccp/psp-dev.h
Normal file
52
drivers/crypto/ccp/psp-dev.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* AMD Platform Security Processor (PSP) interface driver
|
||||
*
|
||||
* Copyright (C) 2017-2019 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Brijesh Singh <brijesh.singh@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef __PSP_DEV_H__
|
||||
#define __PSP_DEV_H__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "sp-dev.h"
|
||||
|
||||
#define PSP_CMDRESP_RESP BIT(31)
|
||||
#define PSP_CMDRESP_ERR_MASK 0xffff
|
||||
|
||||
#define MAX_PSP_NAME_LEN 16
|
||||
|
||||
extern struct psp_device *psp_master;
|
||||
|
||||
typedef void (*psp_irq_handler_t)(int, void *, unsigned int);
|
||||
|
||||
struct psp_device {
|
||||
struct list_head entry;
|
||||
|
||||
struct psp_vdata *vdata;
|
||||
char name[MAX_PSP_NAME_LEN];
|
||||
|
||||
struct device *dev;
|
||||
struct sp_device *sp;
|
||||
|
||||
void __iomem *io_regs;
|
||||
|
||||
psp_irq_handler_t sev_irq_handler;
|
||||
void *sev_irq_data;
|
||||
|
||||
void *sev_data;
|
||||
};
|
||||
|
||||
void psp_set_sev_irq_handler(struct psp_device *psp, psp_irq_handler_t handler,
|
||||
void *data);
|
||||
void psp_clear_sev_irq_handler(struct psp_device *psp);
|
||||
|
||||
struct psp_device *psp_get_master_device(void);
|
||||
|
||||
#endif /* __PSP_DEV_H */
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include <asm/smp.h>
|
||||
|
||||
#include "sp-dev.h"
|
||||
#include "psp-dev.h"
|
||||
#include "sev-dev.h"
|
||||
|
||||
#define DEVICE_NAME "sev"
|
||||
|
@ -32,7 +32,6 @@
|
|||
|
||||
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);
|
||||
|
@ -47,68 +46,45 @@ static int psp_timeout;
|
|||
|
||||
static inline bool sev_version_greater_or_equal(u8 maj, u8 min)
|
||||
{
|
||||
if (psp_master->api_major > maj)
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
|
||||
if (sev->api_major > maj)
|
||||
return true;
|
||||
if (psp_master->api_major == maj && psp_master->api_minor >= min)
|
||||
|
||||
if (sev->api_major == maj && sev->api_minor >= min)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct psp_device *psp_alloc_struct(struct sp_device *sp)
|
||||
static void sev_irq_handler(int irq, void *data, unsigned int status)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct psp_device *psp;
|
||||
|
||||
psp = devm_kzalloc(dev, sizeof(*psp), GFP_KERNEL);
|
||||
if (!psp)
|
||||
return NULL;
|
||||
|
||||
psp->dev = dev;
|
||||
psp->sp = sp;
|
||||
|
||||
snprintf(psp->name, sizeof(psp->name), "psp-%u", sp->ord);
|
||||
|
||||
return psp;
|
||||
}
|
||||
|
||||
static irqreturn_t psp_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct psp_device *psp = data;
|
||||
unsigned int status;
|
||||
struct sev_device *sev = data;
|
||||
int reg;
|
||||
|
||||
/* Read the interrupt status: */
|
||||
status = ioread32(psp->io_regs + psp->vdata->intsts_reg);
|
||||
|
||||
/* Check if it is command completion: */
|
||||
if (!(status & PSP_CMD_COMPLETE))
|
||||
goto done;
|
||||
if (!(status & SEV_CMD_COMPLETE))
|
||||
return;
|
||||
|
||||
/* Check if it is SEV command completion: */
|
||||
reg = ioread32(psp->io_regs + psp->vdata->cmdresp_reg);
|
||||
reg = ioread32(sev->io_regs + sev->psp->vdata->cmdresp_reg);
|
||||
if (reg & PSP_CMDRESP_RESP) {
|
||||
psp->sev_int_rcvd = 1;
|
||||
wake_up(&psp->sev_int_queue);
|
||||
sev->int_rcvd = 1;
|
||||
wake_up(&sev->int_queue);
|
||||
}
|
||||
|
||||
done:
|
||||
/* Clear the interrupt status by writing the same value we read. */
|
||||
iowrite32(status, psp->io_regs + psp->vdata->intsts_reg);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sev_wait_cmd_ioc(struct psp_device *psp,
|
||||
static int sev_wait_cmd_ioc(struct sev_device *sev,
|
||||
unsigned int *reg, unsigned int timeout)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = wait_event_timeout(psp->sev_int_queue,
|
||||
psp->sev_int_rcvd, timeout * HZ);
|
||||
ret = wait_event_timeout(sev->int_queue,
|
||||
sev->int_rcvd, timeout * HZ);
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
*reg = ioread32(psp->io_regs + psp->vdata->cmdresp_reg);
|
||||
*reg = ioread32(sev->io_regs + sev->psp->vdata->cmdresp_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -152,42 +128,45 @@ static int sev_cmd_buffer_len(int cmd)
|
|||
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
struct psp_device *psp = psp_master;
|
||||
struct sev_device *sev;
|
||||
unsigned int phys_lsb, phys_msb;
|
||||
unsigned int reg, ret = 0;
|
||||
|
||||
if (!psp)
|
||||
if (!psp || !psp->sev_data)
|
||||
return -ENODEV;
|
||||
|
||||
if (psp_dead)
|
||||
return -EBUSY;
|
||||
|
||||
sev = psp->sev_data;
|
||||
|
||||
/* 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 timeout %us\n",
|
||||
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);
|
||||
|
||||
iowrite32(phys_lsb, psp->io_regs + psp->vdata->cmdbuff_addr_lo_reg);
|
||||
iowrite32(phys_msb, psp->io_regs + psp->vdata->cmdbuff_addr_hi_reg);
|
||||
iowrite32(phys_lsb, sev->io_regs + psp->vdata->cmdbuff_addr_lo_reg);
|
||||
iowrite32(phys_msb, sev->io_regs + psp->vdata->cmdbuff_addr_hi_reg);
|
||||
|
||||
psp->sev_int_rcvd = 0;
|
||||
sev->int_rcvd = 0;
|
||||
|
||||
reg = cmd;
|
||||
reg <<= PSP_CMDRESP_CMD_SHIFT;
|
||||
reg |= PSP_CMDRESP_IOC;
|
||||
iowrite32(reg, psp->io_regs + psp->vdata->cmdresp_reg);
|
||||
reg <<= SEV_CMDRESP_CMD_SHIFT;
|
||||
reg |= SEV_CMDRESP_IOC;
|
||||
iowrite32(reg, sev->io_regs + psp->vdata->cmdresp_reg);
|
||||
|
||||
/* wait for command completion */
|
||||
ret = sev_wait_cmd_ioc(psp, ®, psp_timeout);
|
||||
ret = sev_wait_cmd_ioc(sev, ®, psp_timeout);
|
||||
if (ret) {
|
||||
if (psp_ret)
|
||||
*psp_ret = 0;
|
||||
|
||||
dev_err(psp->dev, "sev command %#x timed out, disabling PSP \n", cmd);
|
||||
dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd);
|
||||
psp_dead = true;
|
||||
|
||||
return ret;
|
||||
|
@ -199,7 +178,7 @@ static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
|
|||
*psp_ret = reg & PSP_CMDRESP_ERR_MASK;
|
||||
|
||||
if (reg & PSP_CMDRESP_ERR_MASK) {
|
||||
dev_dbg(psp->dev, "sev command %#x failed (%#010x)\n",
|
||||
dev_dbg(sev->dev, "sev command %#x failed (%#010x)\n",
|
||||
cmd, reg & PSP_CMDRESP_ERR_MASK);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
@ -224,19 +203,22 @@ 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_device *sev;
|
||||
int rc = 0;
|
||||
|
||||
if (!psp)
|
||||
if (!psp || !psp->sev_data)
|
||||
return -ENODEV;
|
||||
|
||||
if (psp->sev_state == SEV_STATE_INIT)
|
||||
sev = psp->sev_data;
|
||||
|
||||
if (sev->state == SEV_STATE_INIT)
|
||||
return 0;
|
||||
|
||||
rc = __sev_do_cmd_locked(SEV_CMD_INIT, &psp->init_cmd_buf, error);
|
||||
rc = __sev_do_cmd_locked(SEV_CMD_INIT, &sev->init_cmd_buf, error);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
psp->sev_state = SEV_STATE_INIT;
|
||||
sev->state = SEV_STATE_INIT;
|
||||
|
||||
/* Prepare for first SEV guest launch after INIT */
|
||||
wbinvd_on_all_cpus();
|
||||
|
@ -244,7 +226,7 @@ static int __sev_platform_init_locked(int *error)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev_dbg(psp->dev, "SEV firmware initialized\n");
|
||||
dev_dbg(sev->dev, "SEV firmware initialized\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -263,14 +245,15 @@ EXPORT_SYMBOL_GPL(sev_platform_init);
|
|||
|
||||
static int __sev_platform_shutdown_locked(int *error)
|
||||
{
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
int ret;
|
||||
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_SHUTDOWN, NULL, error);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
psp_master->sev_state = SEV_STATE_UNINIT;
|
||||
dev_dbg(psp_master->dev, "SEV firmware shutdown\n");
|
||||
sev->state = SEV_STATE_UNINIT;
|
||||
dev_dbg(sev->dev, "SEV firmware shutdown\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -288,14 +271,15 @@ 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;
|
||||
int rc;
|
||||
|
||||
rc = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS,
|
||||
&psp_master->status_cmd_buf, error);
|
||||
&sev->status_cmd_buf, error);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*state = psp_master->status_cmd_buf.state;
|
||||
*state = sev->status_cmd_buf.state;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -333,7 +317,8 @@ static int sev_ioctl_do_reset(struct sev_issue_cmd *argp)
|
|||
|
||||
static int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_user_data_status *data = &psp_master->status_cmd_buf;
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
struct sev_user_data_status *data = &sev->status_cmd_buf;
|
||||
int ret;
|
||||
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, data, &argp->error);
|
||||
|
@ -348,12 +333,13 @@ static int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp)
|
|||
|
||||
static int sev_ioctl_do_pek_pdh_gen(int cmd, struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
int rc;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (psp_master->sev_state == SEV_STATE_UNINIT) {
|
||||
if (sev->state == SEV_STATE_UNINIT) {
|
||||
rc = __sev_platform_init_locked(&argp->error);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -364,6 +350,7 @@ static int sev_ioctl_do_pek_pdh_gen(int cmd, struct sev_issue_cmd *argp)
|
|||
|
||||
static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
struct sev_user_data_pek_csr input;
|
||||
struct sev_data_pek_csr *data;
|
||||
void *blob = NULL;
|
||||
|
@ -400,7 +387,7 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp)
|
|||
data->len = input.length;
|
||||
|
||||
cmd:
|
||||
if (psp_master->sev_state == SEV_STATE_UNINIT) {
|
||||
if (sev->state == SEV_STATE_UNINIT) {
|
||||
ret = __sev_platform_init_locked(&argp->error);
|
||||
if (ret)
|
||||
goto e_free_blob;
|
||||
|
@ -443,21 +430,22 @@ 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;
|
||||
int error = 0, ret;
|
||||
|
||||
status = &psp_master->status_cmd_buf;
|
||||
status = &sev->status_cmd_buf;
|
||||
ret = sev_platform_status(status, &error);
|
||||
if (ret) {
|
||||
dev_err(psp_master->dev,
|
||||
dev_err(sev->dev,
|
||||
"SEV: failed to get status. Error: %#x\n", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
psp_master->api_major = status->api_major;
|
||||
psp_master->api_minor = status->api_minor;
|
||||
psp_master->build = status->build;
|
||||
psp_master->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;
|
||||
}
|
||||
|
@ -553,6 +541,7 @@ fw_err:
|
|||
|
||||
static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
struct sev_user_data_pek_cert_import input;
|
||||
struct sev_data_pek_cert_import *data;
|
||||
void *pek_blob, *oca_blob;
|
||||
|
@ -589,7 +578,7 @@ static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp)
|
|||
data->oca_cert_len = input.oca_cert_len;
|
||||
|
||||
/* If platform is not in INIT state then transition it to INIT */
|
||||
if (psp_master->sev_state != SEV_STATE_INIT) {
|
||||
if (sev->state != SEV_STATE_INIT) {
|
||||
ret = __sev_platform_init_locked(&argp->error);
|
||||
if (ret)
|
||||
goto e_free_oca;
|
||||
|
@ -711,13 +700,14 @@ static int sev_ioctl_do_get_id(struct sev_issue_cmd *argp)
|
|||
|
||||
static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp)
|
||||
{
|
||||
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;
|
||||
int ret;
|
||||
|
||||
/* If platform is not in INIT state then transition it to INIT. */
|
||||
if (psp_master->sev_state != SEV_STATE_INIT) {
|
||||
if (sev->state != SEV_STATE_INIT) {
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
|
@ -812,7 +802,7 @@ static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
|
|||
struct sev_issue_cmd input;
|
||||
int ret = -EFAULT;
|
||||
|
||||
if (!psp_master)
|
||||
if (!psp_master || !psp_master->sev_data)
|
||||
return -ENODEV;
|
||||
|
||||
if (ioctl != SEV_ISSUE_CMD)
|
||||
|
@ -911,9 +901,9 @@ static void sev_exit(struct kref *ref)
|
|||
misc_deregister(&misc_dev->misc);
|
||||
}
|
||||
|
||||
static int sev_misc_init(struct psp_device *psp)
|
||||
static int sev_misc_init(struct sev_device *sev)
|
||||
{
|
||||
struct device *dev = psp->dev;
|
||||
struct device *dev = sev->dev;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
|
@ -944,115 +934,61 @@ static int sev_misc_init(struct psp_device *psp)
|
|||
kref_get(&misc_dev->refcount);
|
||||
}
|
||||
|
||||
init_waitqueue_head(&psp->sev_int_queue);
|
||||
psp->sev_misc = misc_dev;
|
||||
init_waitqueue_head(&sev->int_queue);
|
||||
sev->misc = misc_dev;
|
||||
dev_dbg(dev, "registered SEV device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psp_check_sev_support(struct psp_device *psp)
|
||||
int sev_dev_init(struct psp_device *psp)
|
||||
{
|
||||
unsigned int val = ioread32(psp->io_regs + psp->vdata->feature_reg);
|
||||
struct device *dev = psp->dev;
|
||||
struct sev_device *sev;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
/*
|
||||
* Check for a access to the registers. If this read returns
|
||||
* 0xffffffff, it's likely that the system is running a broken
|
||||
* BIOS which disallows access to the device. Stop here and
|
||||
* fail the PSP initialization (but not the load, as the CCP
|
||||
* could get properly initialized).
|
||||
*/
|
||||
if (val == 0xffffffff) {
|
||||
dev_notice(psp->dev, "psp: unable to access the device: you might be running a broken BIOS.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(val & 1)) {
|
||||
/* Device does not support the SEV feature */
|
||||
dev_dbg(psp->dev, "psp does not support SEV\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int psp_dev_init(struct sp_device *sp)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct psp_device *psp;
|
||||
int ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
psp = psp_alloc_struct(sp);
|
||||
if (!psp)
|
||||
sev = devm_kzalloc(dev, sizeof(*sev), GFP_KERNEL);
|
||||
if (!sev)
|
||||
goto e_err;
|
||||
|
||||
sp->psp_data = psp;
|
||||
psp->sev_data = sev;
|
||||
|
||||
psp->vdata = (struct psp_vdata *)sp->dev_vdata->psp_vdata;
|
||||
if (!psp->vdata) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "missing driver data\n");
|
||||
goto e_err;
|
||||
}
|
||||
sev->dev = dev;
|
||||
sev->psp = psp;
|
||||
|
||||
psp->io_regs = sp->io_map;
|
||||
sev->io_regs = psp->io_regs;
|
||||
|
||||
ret = psp_check_sev_support(psp);
|
||||
if (ret)
|
||||
goto e_disable;
|
||||
psp_set_sev_irq_handler(psp, sev_irq_handler, sev);
|
||||
|
||||
/* Disable and clear interrupts until ready */
|
||||
iowrite32(0, psp->io_regs + psp->vdata->inten_reg);
|
||||
iowrite32(-1, psp->io_regs + psp->vdata->intsts_reg);
|
||||
|
||||
/* Request an irq */
|
||||
ret = sp_request_psp_irq(psp->sp, psp_irq_handler, psp->name, psp);
|
||||
if (ret) {
|
||||
dev_err(dev, "psp: unable to allocate an IRQ\n");
|
||||
goto e_err;
|
||||
}
|
||||
|
||||
ret = sev_misc_init(psp);
|
||||
ret = sev_misc_init(sev);
|
||||
if (ret)
|
||||
goto e_irq;
|
||||
|
||||
if (sp->set_psp_master_device)
|
||||
sp->set_psp_master_device(sp);
|
||||
|
||||
/* Enable interrupt */
|
||||
iowrite32(-1, psp->io_regs + psp->vdata->inten_reg);
|
||||
|
||||
dev_notice(dev, "psp enabled\n");
|
||||
dev_notice(dev, "sev enabled\n");
|
||||
|
||||
return 0;
|
||||
|
||||
e_irq:
|
||||
sp_free_psp_irq(psp->sp, psp);
|
||||
psp_clear_sev_irq_handler(psp);
|
||||
e_err:
|
||||
sp->psp_data = NULL;
|
||||
psp->sev_data = NULL;
|
||||
|
||||
dev_notice(dev, "psp initialization failed\n");
|
||||
|
||||
return ret;
|
||||
|
||||
e_disable:
|
||||
sp->psp_data = NULL;
|
||||
dev_notice(dev, "sev initialization failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void psp_dev_destroy(struct sp_device *sp)
|
||||
void sev_dev_destroy(struct psp_device *psp)
|
||||
{
|
||||
struct psp_device *psp = sp->psp_data;
|
||||
struct sev_device *sev = psp->sev_data;
|
||||
|
||||
if (!psp)
|
||||
if (!sev)
|
||||
return;
|
||||
|
||||
if (psp->sev_misc)
|
||||
if (sev->misc)
|
||||
kref_put(&misc_dev->refcount, sev_exit);
|
||||
|
||||
sp_free_psp_irq(sp, psp);
|
||||
psp_clear_sev_irq_handler(psp);
|
||||
}
|
||||
|
||||
int sev_issue_cmd_external_user(struct file *filep, unsigned int cmd,
|
||||
|
@ -1061,21 +997,18 @@ int sev_issue_cmd_external_user(struct file *filep, unsigned int cmd,
|
|||
if (!filep || filep->f_op != &sev_fops)
|
||||
return -EBADF;
|
||||
|
||||
return sev_do_cmd(cmd, data, error);
|
||||
return sev_do_cmd(cmd, data, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
|
||||
|
||||
void psp_pci_init(void)
|
||||
void sev_pci_init(void)
|
||||
{
|
||||
struct sp_device *sp;
|
||||
struct sev_device *sev = psp_master->sev_data;
|
||||
int error, rc;
|
||||
|
||||
sp = sp_get_psp_master_device();
|
||||
if (!sp)
|
||||
if (!sev)
|
||||
return;
|
||||
|
||||
psp_master = sp->psp_data;
|
||||
|
||||
psp_timeout = psp_probe_timeout;
|
||||
|
||||
if (sev_get_api_version())
|
||||
|
@ -1091,13 +1024,13 @@ void psp_pci_init(void)
|
|||
* firmware in INIT or WORKING state.
|
||||
*/
|
||||
|
||||
if (psp_master->sev_state != SEV_STATE_UNINIT) {
|
||||
if (sev->state != SEV_STATE_UNINIT) {
|
||||
sev_platform_shutdown(NULL);
|
||||
psp_master->sev_state = SEV_STATE_UNINIT;
|
||||
sev->state = SEV_STATE_UNINIT;
|
||||
}
|
||||
|
||||
if (sev_version_greater_or_equal(0, 15) &&
|
||||
sev_update_firmware(psp_master->dev) == 0)
|
||||
sev_update_firmware(sev->dev) == 0)
|
||||
sev_get_api_version();
|
||||
|
||||
/* Initialize the platform */
|
||||
|
@ -1110,27 +1043,27 @@ void psp_pci_init(void)
|
|||
* failed and persistent state has been erased.
|
||||
* Retrying INIT command here should succeed.
|
||||
*/
|
||||
dev_dbg(sp->dev, "SEV: retrying INIT command");
|
||||
dev_dbg(sev->dev, "SEV: retrying INIT command");
|
||||
rc = sev_platform_init(&error);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
dev_err(sp->dev, "SEV: failed to INIT error %#x\n", error);
|
||||
dev_err(sev->dev, "SEV: failed to INIT error %#x\n", error);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_info(sp->dev, "SEV API:%d.%d build:%d\n", psp_master->api_major,
|
||||
psp_master->api_minor, psp_master->build);
|
||||
dev_info(sev->dev, "SEV API:%d.%d build:%d\n", sev->api_major,
|
||||
sev->api_minor, sev->build);
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
psp_master = NULL;
|
||||
psp_master->sev_data = NULL;
|
||||
}
|
||||
|
||||
void psp_pci_exit(void)
|
||||
void sev_pci_exit(void)
|
||||
{
|
||||
if (!psp_master)
|
||||
if (!psp_master->sev_data)
|
||||
return;
|
||||
|
||||
sev_platform_shutdown(NULL);
|
||||
|
|
|
@ -25,37 +25,25 @@
|
|||
#include <linux/miscdevice.h>
|
||||
#include <linux/capability.h>
|
||||
|
||||
#include "sp-dev.h"
|
||||
|
||||
#define PSP_CMD_COMPLETE BIT(1)
|
||||
|
||||
#define PSP_CMDRESP_CMD_SHIFT 16
|
||||
#define PSP_CMDRESP_IOC BIT(0)
|
||||
#define PSP_CMDRESP_RESP BIT(31)
|
||||
#define PSP_CMDRESP_ERR_MASK 0xffff
|
||||
|
||||
#define MAX_PSP_NAME_LEN 16
|
||||
#define SEV_CMD_COMPLETE BIT(1)
|
||||
#define SEV_CMDRESP_CMD_SHIFT 16
|
||||
#define SEV_CMDRESP_IOC BIT(0)
|
||||
|
||||
struct sev_misc_dev {
|
||||
struct kref refcount;
|
||||
struct miscdevice misc;
|
||||
};
|
||||
|
||||
struct psp_device {
|
||||
struct list_head entry;
|
||||
|
||||
struct psp_vdata *vdata;
|
||||
char name[MAX_PSP_NAME_LEN];
|
||||
|
||||
struct sev_device {
|
||||
struct device *dev;
|
||||
struct sp_device *sp;
|
||||
struct psp_device *psp;
|
||||
|
||||
void __iomem *io_regs;
|
||||
|
||||
int sev_state;
|
||||
unsigned int sev_int_rcvd;
|
||||
wait_queue_head_t sev_int_queue;
|
||||
struct sev_misc_dev *sev_misc;
|
||||
int state;
|
||||
unsigned int int_rcvd;
|
||||
wait_queue_head_t int_queue;
|
||||
struct sev_misc_dev *misc;
|
||||
struct sev_user_data_status status_cmd_buf;
|
||||
struct sev_data_init init_cmd_buf;
|
||||
|
||||
|
@ -64,4 +52,10 @@ struct psp_device {
|
|||
u8 build;
|
||||
};
|
||||
|
||||
int sev_dev_init(struct psp_device *psp);
|
||||
void sev_dev_destroy(struct psp_device *psp);
|
||||
|
||||
void sev_pci_init(void);
|
||||
void sev_pci_exit(void);
|
||||
|
||||
#endif /* __SEV_DEV_H */
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <linux/ccp.h>
|
||||
|
||||
#include "ccp-dev.h"
|
||||
#include "sev-dev.h"
|
||||
#include "psp-dev.h"
|
||||
|
||||
#define MSIX_VECTORS 2
|
||||
|
||||
|
|
Loading…
Reference in a new issue