diff options
author | Mario Limonciello <mario.limonciello@amd.com> | 2023-06-23 16:49:55 +0300 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2023-07-20 13:14:21 +0300 |
commit | c04cf9e14f109ebcc425c1efd2c01294c52a4d62 (patch) | |
tree | 68c437a1d73a6de139e7024d2abc589ef59a53ae /drivers/crypto/ccp | |
parent | b8440d55f7d4ad2b669902301c87c482faf9a8f4 (diff) | |
download | linux-c04cf9e14f109ebcc425c1efd2c01294c52a4d62.tar.xz |
crypto: ccp - Add support for fetching a nonce for dynamic boost control
Dynamic Boost Control is a feature offered on AMD client platforms that
allows software to request and set power or frequency limits.
Only software that has authenticated with the PSP can retrieve or set
these limits.
Create a character device and ioctl for fetching the nonce. This ioctl
supports optionally passing authentication information which will influence
how many calls the nonce is valid for.
Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/crypto/ccp')
-rw-r--r-- | drivers/crypto/ccp/Makefile | 3 | ||||
-rw-r--r-- | drivers/crypto/ccp/dbc.c | 191 | ||||
-rw-r--r-- | drivers/crypto/ccp/dbc.h | 44 | ||||
-rw-r--r-- | drivers/crypto/ccp/psp-dev.c | 9 | ||||
-rw-r--r-- | drivers/crypto/ccp/psp-dev.h | 1 | ||||
-rw-r--r-- | drivers/crypto/ccp/sp-dev.h | 5 | ||||
-rw-r--r-- | drivers/crypto/ccp/sp-pci.c | 1 |
7 files changed, 253 insertions, 1 deletions
diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile index f6196495e862..aa0ba2d17e1e 100644 --- a/drivers/crypto/ccp/Makefile +++ b/drivers/crypto/ccp/Makefile @@ -11,7 +11,8 @@ ccp-$(CONFIG_PCI) += sp-pci.o ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o \ sev-dev.o \ tee-dev.o \ - platform-access.o + platform-access.o \ + dbc.o obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o ccp-crypto-objs := ccp-crypto-main.o \ diff --git a/drivers/crypto/ccp/dbc.c b/drivers/crypto/ccp/dbc.c new file mode 100644 index 000000000000..f65e93a81e53 --- /dev/null +++ b/drivers/crypto/ccp/dbc.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AMD Secure Processor Dynamic Boost Control interface + * + * Copyright (C) 2023 Advanced Micro Devices, Inc. + * + * Author: Mario Limonciello <mario.limonciello@amd.com> + */ + +#include "dbc.h" + +struct error_map { + u32 psp; + int ret; +}; + +#define DBC_ERROR_ACCESS_DENIED 0x0001 +#define DBC_ERROR_EXCESS_DATA 0x0004 +#define DBC_ERROR_BAD_PARAMETERS 0x0006 +#define DBC_ERROR_BAD_STATE 0x0007 +#define DBC_ERROR_NOT_IMPLEMENTED 0x0009 +#define DBC_ERROR_BUSY 0x000D +#define DBC_ERROR_MESSAGE_FAILURE 0x0307 +#define DBC_ERROR_OVERFLOW 0x300F +#define DBC_ERROR_SIGNATURE_INVALID 0x3072 + +static struct error_map error_codes[] = { + {DBC_ERROR_ACCESS_DENIED, -EACCES}, + {DBC_ERROR_EXCESS_DATA, -E2BIG}, + {DBC_ERROR_BAD_PARAMETERS, -EINVAL}, + {DBC_ERROR_BAD_STATE, -EAGAIN}, + {DBC_ERROR_MESSAGE_FAILURE, -ENOENT}, + {DBC_ERROR_NOT_IMPLEMENTED, -ENOENT}, + {DBC_ERROR_BUSY, -EBUSY}, + {DBC_ERROR_OVERFLOW, -ENFILE}, + {DBC_ERROR_SIGNATURE_INVALID, -EPERM}, + {0x0, 0x0}, +}; + +static int send_dbc_cmd(struct psp_dbc_device *dbc_dev, + enum psp_platform_access_msg msg) +{ + int ret; + + dbc_dev->mbox->req.header.status = 0; + ret = psp_send_platform_access_msg(msg, (struct psp_request *)dbc_dev->mbox); + if (ret == -EIO) { + int i; + + dev_dbg(dbc_dev->dev, + "msg 0x%x failed with PSP error: 0x%x\n", + msg, dbc_dev->mbox->req.header.status); + + for (i = 0; error_codes[i].psp; i++) { + if (dbc_dev->mbox->req.header.status == error_codes[i].psp) + return error_codes[i].ret; + } + } + + return ret; +} + +static int send_dbc_nonce(struct psp_dbc_device *dbc_dev) +{ + int ret; + + dbc_dev->mbox->req.header.payload_size = sizeof(dbc_dev->mbox->dbc_nonce); + ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_NONCE); + if (ret == -EAGAIN) { + dev_dbg(dbc_dev->dev, "retrying get nonce\n"); + ret = send_dbc_cmd(dbc_dev, PSP_DYNAMIC_BOOST_GET_NONCE); + } + + return ret; +} + +void dbc_dev_destroy(struct psp_device *psp) +{ + struct psp_dbc_device *dbc_dev = psp->dbc_data; + + if (!dbc_dev) + return; + + misc_deregister(&dbc_dev->char_dev); + mutex_destroy(&dbc_dev->ioctl_mutex); + psp->dbc_data = NULL; +} + +static long dbc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct psp_device *psp_master = psp_get_master_device(); + void __user *argp = (void __user *)arg; + struct psp_dbc_device *dbc_dev; + int ret; + + if (!psp_master || !psp_master->dbc_data) + return -ENODEV; + dbc_dev = psp_master->dbc_data; + + mutex_lock(&dbc_dev->ioctl_mutex); + + switch (cmd) { + case DBCIOCNONCE: + if (copy_from_user(&dbc_dev->mbox->dbc_nonce.user, argp, + sizeof(struct dbc_user_nonce))) { + ret = -EFAULT; + goto unlock; + } + + ret = send_dbc_nonce(dbc_dev); + if (ret) + goto unlock; + + if (copy_to_user(argp, &dbc_dev->mbox->dbc_nonce.user, + sizeof(struct dbc_user_nonce))) { + ret = -EFAULT; + goto unlock; + } + break; + default: + ret = -EINVAL; + + } +unlock: + mutex_unlock(&dbc_dev->ioctl_mutex); + + return ret; +} + +static const struct file_operations dbc_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dbc_ioctl, +}; + +int dbc_dev_init(struct psp_device *psp) +{ + struct device *dev = psp->dev; + struct psp_dbc_device *dbc_dev; + int ret; + + if (!PSP_FEATURE(psp, DBC)) + return 0; + + dbc_dev = devm_kzalloc(dev, sizeof(*dbc_dev), GFP_KERNEL); + if (!dbc_dev) + return -ENOMEM; + + BUILD_BUG_ON(sizeof(union dbc_buffer) > PAGE_SIZE); + dbc_dev->mbox = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); + if (!dbc_dev->mbox) { + ret = -ENOMEM; + goto cleanup_dev; + } + + psp->dbc_data = dbc_dev; + dbc_dev->dev = dev; + + ret = send_dbc_nonce(dbc_dev); + if (ret == -EACCES) { + dev_dbg(dbc_dev->dev, + "dynamic boost control was previously authenticated\n"); + ret = 0; + } + dev_dbg(dbc_dev->dev, "dynamic boost control is %savailable\n", + ret ? "un" : ""); + if (ret) { + ret = 0; + goto cleanup_mbox; + } + + dbc_dev->char_dev.minor = MISC_DYNAMIC_MINOR; + dbc_dev->char_dev.name = "dbc"; + dbc_dev->char_dev.fops = &dbc_fops; + dbc_dev->char_dev.mode = 0600; + ret = misc_register(&dbc_dev->char_dev); + if (ret) + goto cleanup_mbox; + + mutex_init(&dbc_dev->ioctl_mutex); + + return 0; + +cleanup_mbox: + devm_free_pages(dev, (unsigned long)dbc_dev->mbox); + +cleanup_dev: + psp->dbc_data = NULL; + devm_kfree(dev, dbc_dev); + + return ret; +} diff --git a/drivers/crypto/ccp/dbc.h b/drivers/crypto/ccp/dbc.h new file mode 100644 index 000000000000..1c3a0a078d15 --- /dev/null +++ b/drivers/crypto/ccp/dbc.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AMD Platform Security Processor (PSP) Dynamic Boost Control support + * + * Copyright (C) 2023 Advanced Micro Devices, Inc. + * + * Author: Mario Limonciello <mario.limonciello@amd.com> + */ + +#ifndef __DBC_H__ +#define __DBC_H__ + +#include <uapi/linux/psp-dbc.h> + +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/psp-platform-access.h> + +#include "psp-dev.h" + +struct psp_dbc_device { + struct device *dev; + + union dbc_buffer *mbox; + + struct mutex ioctl_mutex; + + struct miscdevice char_dev; +}; + +struct dbc_nonce { + struct psp_req_buffer_hdr header; + struct dbc_user_nonce user; +} __packed; + +union dbc_buffer { + struct psp_request req; + struct dbc_nonce dbc_nonce; +}; + +void dbc_dev_destroy(struct psp_device *psp); +int dbc_dev_init(struct psp_device *psp); + +#endif /* __DBC_H */ diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c index 3390f0bd6408..d42d7bc62352 100644 --- a/drivers/crypto/ccp/psp-dev.c +++ b/drivers/crypto/ccp/psp-dev.c @@ -15,6 +15,7 @@ #include "sev-dev.h" #include "tee-dev.h" #include "platform-access.h" +#include "dbc.h" struct psp_device *psp_master; @@ -112,6 +113,12 @@ static void psp_init_platform_access(struct psp_device *psp) dev_warn(psp->dev, "platform access init failed: %d\n", ret); return; } + + /* dbc must come after platform access as it tests the feature */ + ret = dbc_dev_init(psp); + if (ret) + dev_warn(psp->dev, "failed to init dynamic boost control: %d\n", + ret); } static int psp_init(struct psp_device *psp) @@ -217,6 +224,8 @@ void psp_dev_destroy(struct sp_device *sp) tee_dev_destroy(psp); + dbc_dev_destroy(psp); + platform_access_dev_destroy(psp); sp_free_psp_irq(sp, psp); diff --git a/drivers/crypto/ccp/psp-dev.h b/drivers/crypto/ccp/psp-dev.h index 505e4bdeaca8..8a4de69399c5 100644 --- a/drivers/crypto/ccp/psp-dev.h +++ b/drivers/crypto/ccp/psp-dev.h @@ -40,6 +40,7 @@ struct psp_device { void *sev_data; void *tee_data; void *platform_access_data; + void *dbc_data; unsigned int capability; }; diff --git a/drivers/crypto/ccp/sp-dev.h b/drivers/crypto/ccp/sp-dev.h index 76c32ee6bd65..2329ad524b49 100644 --- a/drivers/crypto/ccp/sp-dev.h +++ b/drivers/crypto/ccp/sp-dev.h @@ -28,6 +28,10 @@ #define CACHE_NONE 0x00 #define CACHE_WB_NO_ALLOC 0xb7 +#define PLATFORM_FEATURE_DBC 0x1 + +#define PSP_FEATURE(psp, feat) (psp->vdata && psp->vdata->platform_features & PLATFORM_FEATURE_##feat) + /* Structure to hold CCP device data */ struct ccp_device; struct ccp_vdata { @@ -71,6 +75,7 @@ struct psp_vdata { const unsigned int inten_reg; const unsigned int intsts_reg; const unsigned int bootloader_info_reg; + const unsigned int platform_features; }; /* Structure to hold SP device data */ diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c index 205b93d229a9..b6ab56abeb68 100644 --- a/drivers/crypto/ccp/sp-pci.c +++ b/drivers/crypto/ccp/sp-pci.c @@ -470,6 +470,7 @@ static const struct psp_vdata pspv3 = { .feature_reg = 0x109fc, /* C2PMSG_63 */ .inten_reg = 0x10690, /* P2CMSG_INTEN */ .intsts_reg = 0x10694, /* P2CMSG_INTSTS */ + .platform_features = PLATFORM_FEATURE_DBC, }; static const struct psp_vdata pspv4 = { |