crypto: ccp - Add support for fetching a nonce for dynamic boost control
authorMario Limonciello <mario.limonciello@amd.com>
Fri, 23 Jun 2023 13:49:55 +0000 (08:49 -0500)
committerHerbert Xu <herbert@gondor.apana.org.au>
Thu, 20 Jul 2023 10:14:21 +0000 (22:14 +1200)
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>
drivers/crypto/ccp/Makefile
drivers/crypto/ccp/dbc.c [new file with mode: 0644]
drivers/crypto/ccp/dbc.h [new file with mode: 0644]
drivers/crypto/ccp/psp-dev.c
drivers/crypto/ccp/psp-dev.h
drivers/crypto/ccp/sp-dev.h
drivers/crypto/ccp/sp-pci.c
include/linux/psp-platform-access.h
include/uapi/linux/psp-dbc.h [new file with mode: 0644]

index f6196495e862d365bfd0055ed83a139ee42a40e0..aa0ba2d17e1e2202bf6115e0774bf63a802115cb 100644 (file)
@@ -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 (file)
index 0000000..f65e93a
--- /dev/null
@@ -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 (file)
index 0000000..1c3a0a0
--- /dev/null
@@ -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 */
index 3390f0bd64085ffd251ba2396ae79e08fea82d8d..d42d7bc623523dad25f4665d18405b499be2bee9 100644 (file)
@@ -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);
index 505e4bdeaca84ea990f4c42700f4ec383f331ebf..8a4de69399c59abc8271ca346c88c30e0dc2682f 100644 (file)
@@ -40,6 +40,7 @@ struct psp_device {
        void *sev_data;
        void *tee_data;
        void *platform_access_data;
+       void *dbc_data;
 
        unsigned int capability;
 };
index 76c32ee6bd657968cd6f48706ef98e9c51450ba8..2329ad524b4945b29bac80e1b0843c4de6a72a54 100644 (file)
 #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 */
index 205b93d229a95ad8a7458b07265b5d27a65634f6..b6ab56abeb682f89f913558e957ac364d57fbeec 100644 (file)
@@ -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 = {
index 75da8f5f7ad84d538629d30fd169749d51a753ff..53b4a1df5180677f96ab31c56f3e0607fce39bdd 100644 (file)
@@ -8,6 +8,7 @@
 enum psp_platform_access_msg {
        PSP_CMD_NONE = 0x0,
        PSP_I2C_REQ_BUS_CMD = 0x64,
+       PSP_DYNAMIC_BOOST_GET_NONCE,
 };
 
 struct psp_req_buffer_hdr {
diff --git a/include/uapi/linux/psp-dbc.h b/include/uapi/linux/psp-dbc.h
new file mode 100644 (file)
index 0000000..d032f78
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Userspace interface for AMD Dynamic Boost Control (DBC)
+ *
+ * Copyright (C) 2023 Advanced Micro Devices, Inc.
+ *
+ * Author: Mario Limonciello <mario.limonciello@amd.com>
+ */
+
+#ifndef __PSP_DBC_USER_H__
+#define __PSP_DBC_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * DOC: AMD Dynamic Boost Control (DBC) interface
+ */
+
+#define DBC_NONCE_SIZE         16
+#define DBC_SIG_SIZE           32
+
+/**
+ * struct dbc_user_nonce - Nonce exchange structure (input/output).
+ * @auth_needed: Whether the PSP should authenticate this request (input).
+ *               0: no authentication, PSP will return single use nonce.
+ *               1: authentication: PSP will return multi-use nonce.
+ * @nonce:       8 byte value used for future authentication (output).
+ * @signature:   Optional 32 byte signature created by software using a
+ *               previous nonce (input).
+ */
+struct dbc_user_nonce {
+       __u32   auth_needed;
+       __u8    nonce[DBC_NONCE_SIZE];
+       __u8    signature[DBC_SIG_SIZE];
+} __packed;
+
+/**
+ * Dynamic Boost Control (DBC) IOC
+ *
+ * possible return codes for all DBC IOCTLs:
+ *  0:          success
+ *  -EINVAL:    invalid input
+ *  -E2BIG:     excess data passed
+ *  -EFAULT:    failed to copy to/from userspace
+ *  -EBUSY:     mailbox in recovery or in use
+ *  -ENODEV:    driver not bound with PSP device
+ *  -EACCES:    request isn't authorized
+ *  -EINVAL:    invalid parameter
+ *  -ETIMEDOUT: request timed out
+ *  -EAGAIN:    invalid request for state machine
+ *  -ENOENT:    not implemented
+ *  -ENFILE:    overflow
+ *  -EPERM:     invalid signature
+ *  -EIO:       unknown error
+ */
+#define DBC_IOC_TYPE   'D'
+
+/**
+ * DBCIOCNONCE - Fetch a nonce from the PSP for authenticating commands.
+ *               If a nonce is fetched without authentication it can only
+ *               be utilized for one command.
+ *               If a nonce is fetched with authentication it can be used
+ *               for multiple requests.
+ */
+#define DBCIOCNONCE    _IOWR(DBC_IOC_TYPE, 0x1, struct dbc_user_nonce)
+
+#endif /* __PSP_DBC_USER_H__ */