crypto: ccp - Introduce the AMD Secure Processor device

The CCP device is part of the AMD Secure Processor. In order to expand
the usage of the AMD Secure Processor, create a framework that allows
functional components of the AMD Secure Processor to be initialized and
handled appropriately.

Signed-off-by: Brijesh Singh <brijesh.singh@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:
Brijesh Singh 2017-07-06 09:59:14 -05:00 committed by Herbert Xu
parent 970e8303cb
commit 720419f018
12 changed files with 468 additions and 169 deletions

View File

@ -540,11 +540,11 @@ config CRYPTO_DEV_ATMEL_ECC
will be called atmel-ecc. will be called atmel-ecc.
config CRYPTO_DEV_CCP config CRYPTO_DEV_CCP
bool "Support for AMD Cryptographic Coprocessor" bool "Support for AMD Secure Processor"
depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM
help help
The AMD Cryptographic Coprocessor provides hardware offload support The AMD Secure Processor provides support for the Cryptographic Coprocessor
for encryption, hashing and related operations. (CCP) and the Platform Security Processor (PSP) devices.
if CRYPTO_DEV_CCP if CRYPTO_DEV_CCP
source "drivers/crypto/ccp/Kconfig" source "drivers/crypto/ccp/Kconfig"

View File

@ -1,22 +1,29 @@
config CRYPTO_DEV_CCP_DD config CRYPTO_DEV_CCP_DD
tristate "Cryptographic Coprocessor device driver" tristate "Secure Processor device driver"
depends on CRYPTO_DEV_CCP
default m default m
help
Provides AMD Secure Processor device driver.
If you choose 'M' here, this module will be called ccp.
config CRYPTO_DEV_SP_CCP
bool "Cryptographic Coprocessor device"
default y
depends on CRYPTO_DEV_CCP_DD
select HW_RANDOM select HW_RANDOM
select DMA_ENGINE select DMA_ENGINE
select DMADEVICES select DMADEVICES
select CRYPTO_SHA1 select CRYPTO_SHA1
select CRYPTO_SHA256 select CRYPTO_SHA256
help help
Provides the interface to use the AMD Cryptographic Coprocessor Provides the support for AMD Cryptographic Coprocessor (CCP) device
which can be used to offload encryption operations such as SHA, which can be used to offload encryption operations such as SHA, AES
AES and more. If you choose 'M' here, this module will be called and more.
ccp.
config CRYPTO_DEV_CCP_CRYPTO config CRYPTO_DEV_CCP_CRYPTO
tristate "Encryption and hashing offload support" tristate "Encryption and hashing offload support"
depends on CRYPTO_DEV_CCP_DD
default m default m
depends on CRYPTO_DEV_CCP_DD
depends on CRYPTO_DEV_SP_CCP
select CRYPTO_HASH select CRYPTO_HASH
select CRYPTO_BLKCIPHER select CRYPTO_BLKCIPHER
select CRYPTO_AUTHENC select CRYPTO_AUTHENC

View File

@ -1,9 +1,9 @@
obj-$(CONFIG_CRYPTO_DEV_CCP_DD) += ccp.o obj-$(CONFIG_CRYPTO_DEV_CCP_DD) += ccp.o
ccp-objs := ccp-dev.o \ ccp-objs := sp-dev.o ccp-platform.o
ccp-$(CONFIG_CRYPTO_DEV_SP_CCP) += ccp-dev.o \
ccp-ops.o \ ccp-ops.o \
ccp-dev-v3.o \ ccp-dev-v3.o \
ccp-dev-v5.o \ ccp-dev-v5.o \
ccp-platform.o \
ccp-dmaengine.o \ ccp-dmaengine.o \
ccp-debugfs.o ccp-debugfs.o
ccp-$(CONFIG_PCI) += ccp-pci.o ccp-$(CONFIG_PCI) += ccp-pci.o

View File

@ -359,8 +359,7 @@ static void ccp_irq_bh(unsigned long data)
static irqreturn_t ccp_irq_handler(int irq, void *data) static irqreturn_t ccp_irq_handler(int irq, void *data)
{ {
struct device *dev = data; struct ccp_device *ccp = (struct ccp_device *)data;
struct ccp_device *ccp = dev_get_drvdata(dev);
ccp_disable_queue_interrupts(ccp); ccp_disable_queue_interrupts(ccp);
if (ccp->use_tasklet) if (ccp->use_tasklet)
@ -597,6 +596,5 @@ const struct ccp_vdata ccpv3 = {
.version = CCP_VERSION(3, 0), .version = CCP_VERSION(3, 0),
.setup = NULL, .setup = NULL,
.perform = &ccp3_actions, .perform = &ccp3_actions,
.bar = 2,
.offset = 0x20000, .offset = 0x20000,
}; };

View File

@ -769,8 +769,7 @@ static void ccp5_irq_bh(unsigned long data)
static irqreturn_t ccp5_irq_handler(int irq, void *data) static irqreturn_t ccp5_irq_handler(int irq, void *data)
{ {
struct device *dev = data; struct ccp_device *ccp = (struct ccp_device *)data;
struct ccp_device *ccp = dev_get_drvdata(dev);
ccp5_disable_queue_interrupts(ccp); ccp5_disable_queue_interrupts(ccp);
ccp->total_interrupts++; ccp->total_interrupts++;
@ -1113,7 +1112,6 @@ const struct ccp_vdata ccpv5a = {
.version = CCP_VERSION(5, 0), .version = CCP_VERSION(5, 0),
.setup = ccp5_config, .setup = ccp5_config,
.perform = &ccp5_actions, .perform = &ccp5_actions,
.bar = 2,
.offset = 0x0, .offset = 0x0,
}; };
@ -1122,6 +1120,5 @@ const struct ccp_vdata ccpv5b = {
.dma_chan_attr = DMA_PRIVATE, .dma_chan_attr = DMA_PRIVATE,
.setup = ccp5other_config, .setup = ccp5other_config,
.perform = &ccp5_actions, .perform = &ccp5_actions,
.bar = 2,
.offset = 0x0, .offset = 0x0,
}; };

View File

@ -111,13 +111,6 @@ static LIST_HEAD(ccp_units);
static DEFINE_SPINLOCK(ccp_rr_lock); static DEFINE_SPINLOCK(ccp_rr_lock);
static struct ccp_device *ccp_rr; static struct ccp_device *ccp_rr;
/* Ever-increasing value to produce unique unit numbers */
static atomic_t ccp_unit_ordinal;
static unsigned int ccp_increment_unit_ordinal(void)
{
return atomic_inc_return(&ccp_unit_ordinal);
}
/** /**
* ccp_add_device - add a CCP device to the list * ccp_add_device - add a CCP device to the list
* *
@ -465,14 +458,17 @@ int ccp_cmd_queue_thread(void *data)
* *
* @dev: device struct of the CCP * @dev: device struct of the CCP
*/ */
struct ccp_device *ccp_alloc_struct(struct device *dev) struct ccp_device *ccp_alloc_struct(struct sp_device *sp)
{ {
struct device *dev = sp->dev;
struct ccp_device *ccp; struct ccp_device *ccp;
ccp = devm_kzalloc(dev, sizeof(*ccp), GFP_KERNEL); ccp = devm_kzalloc(dev, sizeof(*ccp), GFP_KERNEL);
if (!ccp) if (!ccp)
return NULL; return NULL;
ccp->dev = dev; ccp->dev = dev;
ccp->sp = sp;
ccp->axcache = sp->axcache;
INIT_LIST_HEAD(&ccp->cmd); INIT_LIST_HEAD(&ccp->cmd);
INIT_LIST_HEAD(&ccp->backlog); INIT_LIST_HEAD(&ccp->backlog);
@ -487,9 +483,8 @@ struct ccp_device *ccp_alloc_struct(struct device *dev)
init_waitqueue_head(&ccp->sb_queue); init_waitqueue_head(&ccp->sb_queue);
init_waitqueue_head(&ccp->suspend_queue); init_waitqueue_head(&ccp->suspend_queue);
ccp->ord = ccp_increment_unit_ordinal(); snprintf(ccp->name, MAX_CCP_NAME_LEN, "ccp-%u", sp->ord);
snprintf(ccp->name, MAX_CCP_NAME_LEN, "ccp-%u", ccp->ord); snprintf(ccp->rngname, MAX_CCP_NAME_LEN, "ccp-%u-rng", sp->ord);
snprintf(ccp->rngname, MAX_CCP_NAME_LEN, "ccp-%u-rng", ccp->ord);
return ccp; return ccp;
} }
@ -540,8 +535,9 @@ bool ccp_queues_suspended(struct ccp_device *ccp)
return ccp->cmd_q_count == suspended; return ccp->cmd_q_count == suspended;
} }
int ccp_dev_suspend(struct ccp_device *ccp, pm_message_t state) int ccp_dev_suspend(struct sp_device *sp, pm_message_t state)
{ {
struct ccp_device *ccp = sp->ccp_data;
unsigned long flags; unsigned long flags;
unsigned int i; unsigned int i;
@ -563,8 +559,9 @@ int ccp_dev_suspend(struct ccp_device *ccp, pm_message_t state)
return 0; return 0;
} }
int ccp_dev_resume(struct ccp_device *ccp) int ccp_dev_resume(struct sp_device *sp)
{ {
struct ccp_device *ccp = sp->ccp_data;
unsigned long flags; unsigned long flags;
unsigned int i; unsigned int i;
@ -584,71 +581,54 @@ int ccp_dev_resume(struct ccp_device *ccp)
} }
#endif #endif
int ccp_dev_init(struct ccp_device *ccp) int ccp_dev_init(struct sp_device *sp)
{ {
ccp->io_regs = ccp->io_map + ccp->vdata->offset; struct device *dev = sp->dev;
struct ccp_device *ccp;
int ret;
ret = -ENOMEM;
ccp = ccp_alloc_struct(sp);
if (!ccp)
goto e_err;
sp->ccp_data = ccp;
ccp->vdata = (struct ccp_vdata *)sp->dev_vdata->ccp_vdata;
if (!ccp->vdata || !ccp->vdata->version) {
ret = -ENODEV;
dev_err(dev, "missing driver data\n");
goto e_err;
}
ccp->get_irq = sp->get_irq;
ccp->free_irq = sp->free_irq;
ccp->io_regs = sp->io_map + ccp->vdata->offset;
if (ccp->vdata->setup) if (ccp->vdata->setup)
ccp->vdata->setup(ccp); ccp->vdata->setup(ccp);
return ccp->vdata->perform->init(ccp); ret = ccp->vdata->perform->init(ccp);
if (ret)
goto e_err;
dev_notice(dev, "ccp enabled\n");
return 0;
e_err:
sp->ccp_data = NULL;
dev_notice(dev, "ccp initialization failed\n");
return ret;
} }
void ccp_dev_destroy(struct ccp_device *ccp) void ccp_dev_destroy(struct sp_device *sp)
{ {
struct ccp_device *ccp = sp->ccp_data;
if (!ccp) if (!ccp)
return; return;
ccp->vdata->perform->destroy(ccp); ccp->vdata->perform->destroy(ccp);
} }
static int __init ccp_mod_init(void)
{
#ifdef CONFIG_X86
int ret;
ret = ccp_pci_init();
if (ret)
return ret;
/* Don't leave the driver loaded if init failed */
if (ccp_present() != 0) {
ccp_pci_exit();
return -ENODEV;
}
return 0;
#endif
#ifdef CONFIG_ARM64
int ret;
ret = ccp_platform_init();
if (ret)
return ret;
/* Don't leave the driver loaded if init failed */
if (ccp_present() != 0) {
ccp_platform_exit();
return -ENODEV;
}
return 0;
#endif
return -ENODEV;
}
static void __exit ccp_mod_exit(void)
{
#ifdef CONFIG_X86
ccp_pci_exit();
#endif
#ifdef CONFIG_ARM64
ccp_platform_exit();
#endif
}
module_init(ccp_mod_init);
module_exit(ccp_mod_exit);

View File

@ -27,6 +27,8 @@
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include "sp-dev.h"
#define MAX_CCP_NAME_LEN 16 #define MAX_CCP_NAME_LEN 16
#define MAX_DMAPOOL_NAME_LEN 32 #define MAX_DMAPOOL_NAME_LEN 32
@ -344,6 +346,7 @@ struct ccp_device {
char rngname[MAX_CCP_NAME_LEN]; char rngname[MAX_CCP_NAME_LEN];
struct device *dev; struct device *dev;
struct sp_device *sp;
/* Bus specific device information /* Bus specific device information
*/ */
@ -362,7 +365,6 @@ struct ccp_device {
* them. * them.
*/ */
struct mutex req_mutex ____cacheline_aligned; struct mutex req_mutex ____cacheline_aligned;
void __iomem *io_map;
void __iomem *io_regs; void __iomem *io_regs;
/* Master lists that all cmds are queued on. Because there can be /* Master lists that all cmds are queued on. Because there can be
@ -637,7 +639,7 @@ void ccp_del_device(struct ccp_device *ccp);
extern void ccp_log_error(struct ccp_device *, int); extern void ccp_log_error(struct ccp_device *, int);
struct ccp_device *ccp_alloc_struct(struct device *dev); struct ccp_device *ccp_alloc_struct(struct sp_device *sp);
bool ccp_queues_suspended(struct ccp_device *ccp); bool ccp_queues_suspended(struct ccp_device *ccp);
int ccp_cmd_queue_thread(void *data); int ccp_cmd_queue_thread(void *data);
int ccp_trng_read(struct hwrng *rng, void *data, size_t max, bool wait); int ccp_trng_read(struct hwrng *rng, void *data, size_t max, bool wait);
@ -652,11 +654,6 @@ void ccp_dmaengine_unregister(struct ccp_device *ccp);
void ccp5_debugfs_setup(struct ccp_device *ccp); void ccp5_debugfs_setup(struct ccp_device *ccp);
void ccp5_debugfs_destroy(void); void ccp5_debugfs_destroy(void);
int ccp_dev_init(struct ccp_device *ccp);
void ccp_dev_destroy(struct ccp_device *ccp);
int ccp_dev_suspend(struct ccp_device *ccp, pm_message_t state);
int ccp_dev_resume(struct ccp_device *ccp);
/* Structure for computation functions that are device-specific */ /* Structure for computation functions that are device-specific */
struct ccp_actions { struct ccp_actions {
int (*aes)(struct ccp_op *); int (*aes)(struct ccp_op *);
@ -674,16 +671,6 @@ struct ccp_actions {
irqreturn_t (*irqhandler)(int, void *); irqreturn_t (*irqhandler)(int, void *);
}; };
/* Structure to hold CCP version-specific values */
struct ccp_vdata {
const unsigned int version;
const unsigned int dma_chan_attr;
void (*setup)(struct ccp_device *);
const struct ccp_actions *perform;
const unsigned int bar;
const unsigned int offset;
};
extern const struct ccp_vdata ccpv3_platform; extern const struct ccp_vdata ccpv3_platform;
extern const struct ccp_vdata ccpv3; extern const struct ccp_vdata ccpv3;
extern const struct ccp_vdata ccpv5a; extern const struct ccp_vdata ccpv5a;

View File

@ -40,7 +40,8 @@ struct ccp_pci {
static int ccp_get_msix_irqs(struct ccp_device *ccp) static int ccp_get_msix_irqs(struct ccp_device *ccp)
{ {
struct ccp_pci *ccp_pci = ccp->dev_specific; struct sp_device *sp = ccp->sp;
struct ccp_pci *ccp_pci = sp->dev_specific;
struct device *dev = ccp->dev; struct device *dev = ccp->dev;
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
struct msix_entry msix_entry[MSIX_VECTORS]; struct msix_entry msix_entry[MSIX_VECTORS];
@ -58,11 +59,11 @@ static int ccp_get_msix_irqs(struct ccp_device *ccp)
for (v = 0; v < ccp_pci->msix_count; v++) { for (v = 0; v < ccp_pci->msix_count; v++) {
/* Set the interrupt names and request the irqs */ /* Set the interrupt names and request the irqs */
snprintf(ccp_pci->msix[v].name, name_len, "%s-%u", snprintf(ccp_pci->msix[v].name, name_len, "%s-%u",
ccp->name, v); sp->name, v);
ccp_pci->msix[v].vector = msix_entry[v].vector; ccp_pci->msix[v].vector = msix_entry[v].vector;
ret = request_irq(ccp_pci->msix[v].vector, ret = request_irq(ccp_pci->msix[v].vector,
ccp->vdata->perform->irqhandler, ccp->vdata->perform->irqhandler,
0, ccp_pci->msix[v].name, dev); 0, ccp_pci->msix[v].name, ccp);
if (ret) { if (ret) {
dev_notice(dev, "unable to allocate MSI-X IRQ (%d)\n", dev_notice(dev, "unable to allocate MSI-X IRQ (%d)\n",
ret); ret);
@ -86,6 +87,7 @@ static int ccp_get_msix_irqs(struct ccp_device *ccp)
static int ccp_get_msi_irq(struct ccp_device *ccp) static int ccp_get_msi_irq(struct ccp_device *ccp)
{ {
struct sp_device *sp = ccp->sp;
struct device *dev = ccp->dev; struct device *dev = ccp->dev;
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
int ret; int ret;
@ -96,7 +98,7 @@ static int ccp_get_msi_irq(struct ccp_device *ccp)
ccp->irq = pdev->irq; ccp->irq = pdev->irq;
ret = request_irq(ccp->irq, ccp->vdata->perform->irqhandler, 0, ret = request_irq(ccp->irq, ccp->vdata->perform->irqhandler, 0,
ccp->name, dev); sp->name, ccp);
if (ret) { if (ret) {
dev_notice(dev, "unable to allocate MSI IRQ (%d)\n", ret); dev_notice(dev, "unable to allocate MSI IRQ (%d)\n", ret);
goto e_msi; goto e_msi;
@ -134,17 +136,18 @@ static int ccp_get_irqs(struct ccp_device *ccp)
static void ccp_free_irqs(struct ccp_device *ccp) static void ccp_free_irqs(struct ccp_device *ccp)
{ {
struct ccp_pci *ccp_pci = ccp->dev_specific; struct sp_device *sp = ccp->sp;
struct ccp_pci *ccp_pci = sp->dev_specific;
struct device *dev = ccp->dev; struct device *dev = ccp->dev;
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
if (ccp_pci->msix_count) { if (ccp_pci->msix_count) {
while (ccp_pci->msix_count--) while (ccp_pci->msix_count--)
free_irq(ccp_pci->msix[ccp_pci->msix_count].vector, free_irq(ccp_pci->msix[ccp_pci->msix_count].vector,
dev); ccp);
pci_disable_msix(pdev); pci_disable_msix(pdev);
} else if (ccp->irq) { } else if (ccp->irq) {
free_irq(ccp->irq, dev); free_irq(ccp->irq, ccp);
pci_disable_msi(pdev); pci_disable_msi(pdev);
} }
ccp->irq = 0; ccp->irq = 0;
@ -152,7 +155,7 @@ static void ccp_free_irqs(struct ccp_device *ccp)
static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
struct ccp_device *ccp; struct sp_device *sp;
struct ccp_pci *ccp_pci; struct ccp_pci *ccp_pci;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
void __iomem * const *iomap_table; void __iomem * const *iomap_table;
@ -160,23 +163,23 @@ static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
int ret; int ret;
ret = -ENOMEM; ret = -ENOMEM;
ccp = ccp_alloc_struct(dev); sp = sp_alloc_struct(dev);
if (!ccp) if (!sp)
goto e_err; goto e_err;
ccp_pci = devm_kzalloc(dev, sizeof(*ccp_pci), GFP_KERNEL); ccp_pci = devm_kzalloc(dev, sizeof(*ccp_pci), GFP_KERNEL);
if (!ccp_pci) if (!ccp_pci)
goto e_err; goto e_err;
ccp->dev_specific = ccp_pci; sp->dev_specific = ccp_pci;
ccp->vdata = (struct ccp_vdata *)id->driver_data; sp->dev_vdata = (struct sp_dev_vdata *)id->driver_data;
if (!ccp->vdata || !ccp->vdata->version) { if (!sp->dev_vdata) {
ret = -ENODEV; ret = -ENODEV;
dev_err(dev, "missing driver data\n"); dev_err(dev, "missing driver data\n");
goto e_err; goto e_err;
} }
ccp->get_irq = ccp_get_irqs; sp->get_irq = ccp_get_irqs;
ccp->free_irq = ccp_free_irqs; sp->free_irq = ccp_free_irqs;
ret = pcim_enable_device(pdev); ret = pcim_enable_device(pdev);
if (ret) { if (ret) {
@ -198,8 +201,8 @@ static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto e_err; goto e_err;
} }
ccp->io_map = iomap_table[ccp->vdata->bar]; sp->io_map = iomap_table[sp->dev_vdata->bar];
if (!ccp->io_map) { if (!sp->io_map) {
dev_err(dev, "ioremap failed\n"); dev_err(dev, "ioremap failed\n");
ret = -ENOMEM; ret = -ENOMEM;
goto e_err; goto e_err;
@ -217,9 +220,9 @@ static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
} }
} }
dev_set_drvdata(dev, ccp); dev_set_drvdata(dev, sp);
ret = ccp_dev_init(ccp); ret = sp_init(sp);
if (ret) if (ret)
goto e_err; goto e_err;
@ -235,12 +238,12 @@ static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static void ccp_pci_remove(struct pci_dev *pdev) static void ccp_pci_remove(struct pci_dev *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ccp_device *ccp = dev_get_drvdata(dev); struct sp_device *sp = dev_get_drvdata(dev);
if (!ccp) if (!sp)
return; return;
ccp_dev_destroy(ccp); sp_destroy(sp);
dev_notice(dev, "disabled\n"); dev_notice(dev, "disabled\n");
} }
@ -249,24 +252,44 @@ static void ccp_pci_remove(struct pci_dev *pdev)
static int ccp_pci_suspend(struct pci_dev *pdev, pm_message_t state) static int ccp_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ccp_device *ccp = dev_get_drvdata(dev); struct sp_device *sp = dev_get_drvdata(dev);
return ccp_dev_suspend(ccp, state); return sp_suspend(sp, state);
} }
static int ccp_pci_resume(struct pci_dev *pdev) static int ccp_pci_resume(struct pci_dev *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ccp_device *ccp = dev_get_drvdata(dev); struct sp_device *sp = dev_get_drvdata(dev);
return ccp_dev_resume(ccp); return sp_resume(sp);
} }
#endif #endif
static const struct sp_dev_vdata dev_vdata[] = {
{
.bar = 2,
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
.ccp_vdata = &ccpv3,
#endif
},
{
.bar = 2,
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
.ccp_vdata = &ccpv5a,
#endif
},
{
.bar = 2,
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
.ccp_vdata = &ccpv5b,
#endif
},
};
static const struct pci_device_id ccp_pci_table[] = { static const struct pci_device_id ccp_pci_table[] = {
{ PCI_VDEVICE(AMD, 0x1537), (kernel_ulong_t)&ccpv3 }, { PCI_VDEVICE(AMD, 0x1537), (kernel_ulong_t)&dev_vdata[0] },
{ PCI_VDEVICE(AMD, 0x1456), (kernel_ulong_t)&ccpv5a }, { PCI_VDEVICE(AMD, 0x1456), (kernel_ulong_t)&dev_vdata[1] },
{ PCI_VDEVICE(AMD, 0x1468), (kernel_ulong_t)&ccpv5b }, { PCI_VDEVICE(AMD, 0x1468), (kernel_ulong_t)&dev_vdata[2] },
/* Last entry must be zero */ /* Last entry must be zero */
{ 0, } { 0, }
}; };

View File

@ -35,26 +35,26 @@ struct ccp_platform {
static const struct acpi_device_id ccp_acpi_match[]; static const struct acpi_device_id ccp_acpi_match[];
static const struct of_device_id ccp_of_match[]; static const struct of_device_id ccp_of_match[];
static struct ccp_vdata *ccp_get_of_version(struct platform_device *pdev) static struct sp_dev_vdata *ccp_get_of_version(struct platform_device *pdev)
{ {
#ifdef CONFIG_OF #ifdef CONFIG_OF
const struct of_device_id *match; const struct of_device_id *match;
match = of_match_node(ccp_of_match, pdev->dev.of_node); match = of_match_node(ccp_of_match, pdev->dev.of_node);
if (match && match->data) if (match && match->data)
return (struct ccp_vdata *)match->data; return (struct sp_dev_vdata *)match->data;
#endif #endif
return NULL; return NULL;
} }
static struct ccp_vdata *ccp_get_acpi_version(struct platform_device *pdev) static struct sp_dev_vdata *ccp_get_acpi_version(struct platform_device *pdev)
{ {
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
const struct acpi_device_id *match; const struct acpi_device_id *match;
match = acpi_match_device(ccp_acpi_match, &pdev->dev); match = acpi_match_device(ccp_acpi_match, &pdev->dev);
if (match && match->driver_data) if (match && match->driver_data)
return (struct ccp_vdata *)match->driver_data; return (struct sp_dev_vdata *)match->driver_data;
#endif #endif
return NULL; return NULL;
} }
@ -73,7 +73,7 @@ static int ccp_get_irq(struct ccp_device *ccp)
ccp->irq = ret; ccp->irq = ret;
ret = request_irq(ccp->irq, ccp->vdata->perform->irqhandler, 0, ret = request_irq(ccp->irq, ccp->vdata->perform->irqhandler, 0,
ccp->name, dev); ccp->name, ccp);
if (ret) { if (ret) {
dev_notice(dev, "unable to allocate IRQ (%d)\n", ret); dev_notice(dev, "unable to allocate IRQ (%d)\n", ret);
return ret; return ret;
@ -99,14 +99,12 @@ static int ccp_get_irqs(struct ccp_device *ccp)
static void ccp_free_irqs(struct ccp_device *ccp) static void ccp_free_irqs(struct ccp_device *ccp)
{ {
struct device *dev = ccp->dev; free_irq(ccp->irq, ccp);
free_irq(ccp->irq, dev);
} }
static int ccp_platform_probe(struct platform_device *pdev) static int ccp_platform_probe(struct platform_device *pdev)
{ {
struct ccp_device *ccp; struct sp_device *sp;
struct ccp_platform *ccp_platform; struct ccp_platform *ccp_platform;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
enum dev_dma_attr attr; enum dev_dma_attr attr;
@ -114,32 +112,31 @@ static int ccp_platform_probe(struct platform_device *pdev)
int ret; int ret;
ret = -ENOMEM; ret = -ENOMEM;
ccp = ccp_alloc_struct(dev); sp = sp_alloc_struct(dev);
if (!ccp) if (!sp)
goto e_err; goto e_err;
ccp_platform = devm_kzalloc(dev, sizeof(*ccp_platform), GFP_KERNEL); ccp_platform = devm_kzalloc(dev, sizeof(*ccp_platform), GFP_KERNEL);
if (!ccp_platform) if (!ccp_platform)
goto e_err; goto e_err;
ccp->dev_specific = ccp_platform; sp->dev_specific = ccp_platform;
ccp->vdata = pdev->dev.of_node ? ccp_get_of_version(pdev) sp->dev_vdata = pdev->dev.of_node ? ccp_get_of_version(pdev)
: ccp_get_acpi_version(pdev); : ccp_get_acpi_version(pdev);
if (!ccp->vdata || !ccp->vdata->version) { if (!sp->dev_vdata) {
ret = -ENODEV; ret = -ENODEV;
dev_err(dev, "missing driver data\n"); dev_err(dev, "missing driver data\n");
goto e_err; goto e_err;
} }
ccp->get_irq = ccp_get_irqs; sp->get_irq = ccp_get_irqs;
ccp->free_irq = ccp_free_irqs; sp->free_irq = ccp_free_irqs;
ior = platform_get_resource(pdev, IORESOURCE_MEM, 0); ior = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ccp->io_map = devm_ioremap_resource(dev, ior); sp->io_map = devm_ioremap_resource(dev, ior);
if (IS_ERR(ccp->io_map)) { if (IS_ERR(sp->io_map)) {
ret = PTR_ERR(ccp->io_map); ret = PTR_ERR(sp->io_map);
goto e_err; goto e_err;
} }
ccp->io_regs = ccp->io_map;
attr = device_get_dma_attr(dev); attr = device_get_dma_attr(dev);
if (attr == DEV_DMA_NOT_SUPPORTED) { if (attr == DEV_DMA_NOT_SUPPORTED) {
@ -149,9 +146,9 @@ static int ccp_platform_probe(struct platform_device *pdev)
ccp_platform->coherent = (attr == DEV_DMA_COHERENT); ccp_platform->coherent = (attr == DEV_DMA_COHERENT);
if (ccp_platform->coherent) if (ccp_platform->coherent)
ccp->axcache = CACHE_WB_NO_ALLOC; sp->axcache = CACHE_WB_NO_ALLOC;
else else
ccp->axcache = CACHE_NONE; sp->axcache = CACHE_NONE;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
if (ret) { if (ret) {
@ -159,9 +156,9 @@ static int ccp_platform_probe(struct platform_device *pdev)
goto e_err; goto e_err;
} }
dev_set_drvdata(dev, ccp); dev_set_drvdata(dev, sp);
ret = ccp_dev_init(ccp); ret = sp_init(sp);
if (ret) if (ret)
goto e_err; goto e_err;
@ -177,9 +174,9 @@ static int ccp_platform_probe(struct platform_device *pdev)
static int ccp_platform_remove(struct platform_device *pdev) static int ccp_platform_remove(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ccp_device *ccp = dev_get_drvdata(dev); struct sp_device *sp = dev_get_drvdata(dev);
ccp_dev_destroy(ccp); sp_destroy(sp);
dev_notice(dev, "disabled\n"); dev_notice(dev, "disabled\n");
@ -191,23 +188,32 @@ static int ccp_platform_suspend(struct platform_device *pdev,
pm_message_t state) pm_message_t state)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ccp_device *ccp = dev_get_drvdata(dev); struct sp_device *sp = dev_get_drvdata(dev);
return ccp_dev_suspend(ccp, state); return sp_suspend(sp, state);
} }
static int ccp_platform_resume(struct platform_device *pdev) static int ccp_platform_resume(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ccp_device *ccp = dev_get_drvdata(dev); struct sp_device *sp = dev_get_drvdata(dev);
return ccp_dev_resume(ccp); return sp_resume(sp);
} }
#endif #endif
static const struct sp_dev_vdata dev_vdata[] = {
{
.bar = 0,
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
.ccp_vdata = &ccpv3_platform,
#endif
},
};
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct acpi_device_id ccp_acpi_match[] = { static const struct acpi_device_id ccp_acpi_match[] = {
{ "AMDI0C00", (kernel_ulong_t)&ccpv3 }, { "AMDI0C00", (kernel_ulong_t)&dev_vdata[0] },
{ }, { },
}; };
MODULE_DEVICE_TABLE(acpi, ccp_acpi_match); MODULE_DEVICE_TABLE(acpi, ccp_acpi_match);
@ -216,7 +222,7 @@ MODULE_DEVICE_TABLE(acpi, ccp_acpi_match);
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id ccp_of_match[] = { static const struct of_device_id ccp_of_match[] = {
{ .compatible = "amd,ccp-seattle-v1a", { .compatible = "amd,ccp-seattle-v1a",
.data = (const void *)&ccpv3_platform }, .data = (const void *)&dev_vdata[0] },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, ccp_of_match); MODULE_DEVICE_TABLE(of, ccp_of_match);

182
drivers/crypto/ccp/sp-dev.c Normal file
View File

@ -0,0 +1,182 @@
/*
* AMD Secure Processor driver
*
* Copyright (C) 2017 Advanced Micro Devices, Inc.
*
* Author: Tom Lendacky <thomas.lendacky@amd.com>
* Author: Gary R Hook <gary.hook@amd.com>
* Author: Brijesh Singh <brijesh.singh@amd.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#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/ccp.h>
#include "ccp-dev.h"
#include "sp-dev.h"
MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
MODULE_AUTHOR("Gary R Hook <gary.hook@amd.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.1.0");
MODULE_DESCRIPTION("AMD Secure Processor driver");
/* List of SPs, SP count, read-write access lock, and access functions
*
* Lock structure: get sp_unit_lock for reading whenever we need to
* examine the SP list.
*/
static DEFINE_RWLOCK(sp_unit_lock);
static LIST_HEAD(sp_units);
/* Ever-increasing value to produce unique unit numbers */
static atomic_t sp_ordinal;
static void sp_add_device(struct sp_device *sp)
{
unsigned long flags;
write_lock_irqsave(&sp_unit_lock, flags);
list_add_tail(&sp->entry, &sp_units);
write_unlock_irqrestore(&sp_unit_lock, flags);
}
static void sp_del_device(struct sp_device *sp)
{
unsigned long flags;
write_lock_irqsave(&sp_unit_lock, flags);
list_del(&sp->entry);
write_unlock_irqrestore(&sp_unit_lock, flags);
}
/**
* sp_alloc_struct - allocate and initialize the sp_device struct
*
* @dev: device struct of the SP
*/
struct sp_device *sp_alloc_struct(struct device *dev)
{
struct sp_device *sp;
sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
if (!sp)
return NULL;
sp->dev = dev;
sp->ord = atomic_inc_return(&sp_ordinal);
snprintf(sp->name, SP_MAX_NAME_LEN, "sp-%u", sp->ord);
return sp;
}
int sp_init(struct sp_device *sp)
{
sp_add_device(sp);
if (sp->dev_vdata->ccp_vdata)
ccp_dev_init(sp);
return 0;
}
void sp_destroy(struct sp_device *sp)
{
if (sp->dev_vdata->ccp_vdata)
ccp_dev_destroy(sp);
sp_del_device(sp);
}
#ifdef CONFIG_PM
int sp_suspend(struct sp_device *sp, pm_message_t state)
{
int ret;
if (sp->dev_vdata->ccp_vdata) {
ret = ccp_dev_suspend(sp, state);
if (ret)
return ret;
}
return 0;
}
int sp_resume(struct sp_device *sp)
{
int ret;
if (sp->dev_vdata->ccp_vdata) {
ret = ccp_dev_resume(sp);
if (ret)
return ret;
}
return 0;
}
#endif
static int __init sp_mod_init(void)
{
#ifdef CONFIG_X86
int ret;
ret = ccp_pci_init();
if (ret)
return ret;
/* Don't leave the driver loaded if init failed */
if (ccp_present() != 0) {
ccp_pci_exit();
return -ENODEV;
}
return 0;
#endif
#ifdef CONFIG_ARM64
int ret;
ret = ccp_platform_init();
if (ret)
return ret;
/* Don't leave the driver loaded if init failed */
if (ccp_present() != 0) {
ccp_platform_exit();
return -ENODEV;
}
return 0;
#endif
return -ENODEV;
}
static void __exit sp_mod_exit(void)
{
#ifdef CONFIG_X86
ccp_pci_exit();
#endif
#ifdef CONFIG_ARM64
ccp_platform_exit();
#endif
}
module_init(sp_mod_init);
module_exit(sp_mod_exit);

120
drivers/crypto/ccp/sp-dev.h Normal file
View File

@ -0,0 +1,120 @@
/*
* AMD Secure Processor driver
*
* Copyright (C) 2017 Advanced Micro Devices, Inc.
*
* Author: Tom Lendacky <thomas.lendacky@amd.com>
* Author: Gary R Hook <gary.hook@amd.com>
* Author: Brijesh Singh <brijesh.singh@amd.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SP_DEV_H__
#define __SP_DEV_H__
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/dmapool.h>
#include <linux/hw_random.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#define SP_MAX_NAME_LEN 32
#define CACHE_NONE 0x00
#define CACHE_WB_NO_ALLOC 0xb7
/* Structure to hold CCP device data */
struct ccp_device;
struct ccp_vdata {
const unsigned int version;
const unsigned int dma_chan_attr;
void (*setup)(struct ccp_device *);
const struct ccp_actions *perform;
const unsigned int offset;
};
/* Structure to hold SP device data */
struct sp_dev_vdata {
const unsigned int bar;
const struct ccp_vdata *ccp_vdata;
void *psp_vdata;
};
struct sp_device {
struct list_head entry;
struct device *dev;
struct sp_dev_vdata *dev_vdata;
unsigned int ord;
char name[SP_MAX_NAME_LEN];
/* Bus specific device information */
void *dev_specific;
/* I/O area used for device communication. */
void __iomem *io_map;
/* DMA caching attribute support */
unsigned int axcache;
bool irq_registered;
int (*get_irq)(struct ccp_device *ccp);
void (*free_irq)(struct ccp_device *ccp);
void *ccp_data;
void *psp_data;
};
int sp_pci_init(void);
void sp_pci_exit(void);
int sp_platform_init(void);
void sp_platform_exit(void);
struct sp_device *sp_alloc_struct(struct device *dev);
int sp_init(struct sp_device *sp);
void sp_destroy(struct sp_device *sp);
struct sp_device *sp_get_master(void);
int sp_suspend(struct sp_device *sp, pm_message_t state);
int sp_resume(struct sp_device *sp);
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
int ccp_dev_init(struct sp_device *sp);
void ccp_dev_destroy(struct sp_device *sp);
int ccp_dev_suspend(struct sp_device *sp, pm_message_t state);
int ccp_dev_resume(struct sp_device *sp);
#else /* !CONFIG_CRYPTO_DEV_SP_CCP */
static inline int ccp_dev_init(struct sp_device *sp)
{
return 0;
}
static inline void ccp_dev_destroy(struct sp_device *sp) { }
static inline int ccp_dev_suspend(struct sp_device *sp, pm_message_t state)
{
return 0;
}
static inline int ccp_dev_resume(struct sp_device *sp)
{
return 0;
}
#endif /* CONFIG_CRYPTO_DEV_SP_CCP */
#endif

View File

@ -23,8 +23,7 @@
struct ccp_device; struct ccp_device;
struct ccp_cmd; struct ccp_cmd;
#if defined(CONFIG_CRYPTO_DEV_CCP_DD) || \ #if defined(CONFIG_CRYPTO_DEV_SP_CCP)
defined(CONFIG_CRYPTO_DEV_CCP_DD_MODULE)
/** /**
* ccp_present - check if a CCP device is present * ccp_present - check if a CCP device is present
@ -70,7 +69,7 @@ unsigned int ccp_version(void);
*/ */
int ccp_enqueue_cmd(struct ccp_cmd *cmd); int ccp_enqueue_cmd(struct ccp_cmd *cmd);
#else /* CONFIG_CRYPTO_DEV_CCP_DD is not enabled */ #else /* CONFIG_CRYPTO_DEV_CCP_SP_DEV is not enabled */
static inline int ccp_present(void) static inline int ccp_present(void)
{ {
@ -87,7 +86,7 @@ static inline int ccp_enqueue_cmd(struct ccp_cmd *cmd)
return -ENODEV; return -ENODEV;
} }
#endif /* CONFIG_CRYPTO_DEV_CCP_DD */ #endif /* CONFIG_CRYPTO_DEV_SP_CCP */
/***** AES engine *****/ /***** AES engine *****/