From: niuyongwen <niuyongwen(a)hygon.cn>
The existing kernel supports only interrupt for the mailbox interface for X86
sending commands to PSP and PSP to ack, e.g. the SEV commands. However, some
PSP-based security modules in Hygon CPU, such as TPCM and TDM(Trusted Dynamic
Measuring), needs sending commands/notifications proactively to X86 core via
interrupt and a 2nd mailbox interface. Similar to the existing one, the 2nd
mailbox consists of a 32-bits command register and two 32-bits data registers.
The PSP interrupt handling needs to add this interrupt support; besides, in
order to support user defined command handler, a callback registration function
is also provided. Up to 16 command callbacks is supported, which are indexed by
command IDs. Currently, command ID 0 is assigned to TPCM and 1 to TDM, while others
are reserved.
Change-Id: I3044232cef5ebe4500f4a2ecd6854911bdd6cea7
Signed-off-by: niuyongwen <niuyongwen(a)hygon.cn>
---
drivers/crypto/ccp/Kconfig | 8 +++
drivers/crypto/ccp/psp-dev.c | 103 ++++++++++++++++++++++++++++++++++-
drivers/crypto/ccp/psp-dev.h | 5 ++
drivers/crypto/ccp/sp-dev.h | 5 ++
drivers/crypto/ccp/sp-pci.c | 10 ++++
include/linux/psp-sev.h | 10 ++++
6 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
index b9dfae47aefd..fc9cdfc72706 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -44,3 +44,11 @@ config CRYPTO_DEV_SP_PSP
management commands in Secure Encrypted Virtualization (SEV) mode,
along with software-based Trusted Execution Environment (TEE) to
enable third-party trusted applications.
+
+config HYGON_PSP2CPU_CMD
+ bool "Hygon PSP2CPU Command Interface"
+ default y
+ depends on CPU_SUP_HYGON
+ depends on CRYPTO_DEV_SP_PSP
+ help
+ Hygon PSP2CPU Command Support
diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c
index cf65991723c4..e1ef5e39a131 100644
--- a/drivers/crypto/ccp/psp-dev.c
+++ b/drivers/crypto/ccp/psp-dev.c
@@ -66,6 +66,98 @@ static struct psp_device *psp_alloc_struct(struct sp_device *sp)
return psp;
}
+#ifdef CONFIG_HYGON_PSP2CPU_CMD
+static DEFINE_SPINLOCK(p2c_notifier_lock);
+static p2c_notifier_t p2c_notifiers[P2C_NOTIFIERS_MAX] = {NULL};
+int psp_register_cmd_notifier(uint32_t cmd_id, int (*notifier)(uint32_t id, uint64_t
data))
+{
+ int ret = -ENODEV;
+ unsigned long flags;
+
+ spin_lock_irqsave(&p2c_notifier_lock, flags);
+ if (cmd_id < P2C_NOTIFIERS_MAX && !p2c_notifiers[cmd_id]) {
+ p2c_notifiers[cmd_id] = notifier;
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&p2c_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_register_cmd_notifier);
+
+int psp_unregister_cmd_notifier(uint32_t cmd_id, int (*notifier)(uint32_t id, uint64_t
data))
+{
+ int ret = -ENODEV;
+ unsigned long flags;
+
+ spin_lock_irqsave(&p2c_notifier_lock, flags);
+ if (cmd_id < P2C_NOTIFIERS_MAX && p2c_notifiers[cmd_id] == notifier) {
+ p2c_notifiers[cmd_id] = NULL;
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&p2c_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_unregister_cmd_notifier);
+
+#define PSP2CPU_MAX_LOOP 100
+static irqreturn_t psp_irq_handler_hygon(int irq, void *data)
+{
+ struct psp_device *psp = data;
+ unsigned int status;
+ int reg;
+ unsigned long flags;
+ int count = 0;
+ uint32_t p2c_cmd;
+ uint32_t p2c_lo_data;
+ uint32_t p2c_hi_data;
+ uint64_t p2c_data;
+
+ /* Read the interrupt status: */
+ status = ioread32(psp->io_regs + psp->vdata->intsts_reg);
+
+ while (status && (count++ < PSP2CPU_MAX_LOOP)) {
+ /* Clear the interrupt status by writing the same value we read. */
+ iowrite32(status, psp->io_regs + psp->vdata->intsts_reg);
+
+ /* Check if it is command completion: */
+ if (status & PSP_CMD_COMPLETE) {
+ /* Check if it is SEV command completion: */
+ reg = ioread32(psp->io_regs + psp->vdata->cmdresp_reg);
+ if (reg & PSP_CMDRESP_RESP) {
+ psp->sev_int_rcvd = 1;
+ wake_up(&psp->sev_int_queue);
+ }
+ }
+
+ if (status & PSP_X86_CMD) {
+ /* Check if it is P2C command completion: */
+ reg = ioread32(psp->io_regs + psp->vdata->p2c_cmdresp_reg);
+ if (!(reg & PSP_CMDRESP_RESP)) {
+ p2c_lo_data = ioread32(psp->io_regs + psp->vdata->p2c_cmdbuff_addr_lo_reg);
+ p2c_hi_data = ioread32(psp->io_regs + psp->vdata->p2c_cmdbuff_addr_hi_reg);
+ p2c_data = (((uint64_t)(p2c_hi_data) << 32) + ((uint64_t)(p2c_lo_data)));
+ p2c_cmd = (uint32_t)(reg & PSP_CMDRESP_IOC);
+ if (p2c_cmd < P2C_NOTIFIERS_MAX) {
+ spin_lock_irqsave(&p2c_notifier_lock, flags);
+ if (p2c_notifiers[p2c_cmd])
+ p2c_notifiers[p2c_cmd](p2c_cmd, p2c_data);
+
+ spin_unlock_irqrestore(&p2c_notifier_lock, flags);
+ }
+
+ reg |= PSP_CMDRESP_RESP;
+ iowrite32(reg, psp->io_regs + psp->vdata->p2c_cmdresp_reg);
+ }
+ }
+ status = ioread32(psp->io_regs + psp->vdata->intsts_reg);
+ }
+
+ return IRQ_HANDLED;
+}
+#endif
+
static irqreturn_t psp_irq_handler(int irq, void *data)
{
struct psp_device *psp = data;
@@ -840,6 +932,7 @@ static int sev_init(struct psp_device *psp)
int psp_dev_init(struct sp_device *sp)
{
struct device *dev = sp->dev;
+ struct pci_dev *pdev = to_pci_dev(dev);
struct psp_device *psp;
int ret;
@@ -864,7 +957,15 @@ int psp_dev_init(struct sp_device *sp)
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 (pdev->vendor == PCI_VENDOR_ID_HYGON) {
+#ifdef CONFIG_HYGON_PSP2CPU_CMD
+ ret = sp_request_psp_irq(psp->sp, psp_irq_handler_hygon, psp->name, psp);
+#else
+ ret = sp_request_psp_irq(psp->sp, psp_irq_handler, psp->name, psp);
+#endif
+ } else {
+ 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;
diff --git a/drivers/crypto/ccp/psp-dev.h b/drivers/crypto/ccp/psp-dev.h
index 8b53a9674ecb..389c04a7bf53 100644
--- a/drivers/crypto/ccp/psp-dev.h
+++ b/drivers/crypto/ccp/psp-dev.h
@@ -39,6 +39,11 @@
#define MAX_PSP_NAME_LEN 16
+#ifdef CONFIG_HYGON_PSP2CPU_CMD
+#define PSP_X86_CMD BIT(2)
+#define P2C_NOTIFIERS_MAX 16
+#endif
+
struct sev_misc_dev {
struct kref refcount;
struct miscdevice misc;
diff --git a/drivers/crypto/ccp/sp-dev.h b/drivers/crypto/ccp/sp-dev.h
index 14398cad1625..847614e8831a 100644
--- a/drivers/crypto/ccp/sp-dev.h
+++ b/drivers/crypto/ccp/sp-dev.h
@@ -50,6 +50,11 @@ struct psp_vdata {
const unsigned int feature_reg;
const unsigned int inten_reg;
const unsigned int intsts_reg;
+#ifdef CONFIG_HYGON_PSP2CPU_CMD
+ const unsigned int p2c_cmdresp_reg;
+ const unsigned int p2c_cmdbuff_addr_lo_reg;
+ const unsigned int p2c_cmdbuff_addr_hi_reg;
+#endif
};
/* Structure to hold SP device data */
diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c
index edfdd0bffb5a..7f0db9782ae4 100644
--- a/drivers/crypto/ccp/sp-pci.c
+++ b/drivers/crypto/ccp/sp-pci.c
@@ -276,6 +276,11 @@ static const struct psp_vdata pspv1 = {
.feature_reg = 0x105fc,
.inten_reg = 0x10610,
.intsts_reg = 0x10614,
+#ifdef CONFIG_HYGON_PSP2CPU_CMD
+ .p2c_cmdresp_reg = 0x105e8,
+ .p2c_cmdbuff_addr_lo_reg = 0x105ec,
+ .p2c_cmdbuff_addr_hi_reg = 0x105f0,
+#endif
};
static const struct psp_vdata pspv2 = {
@@ -285,6 +290,11 @@ static const struct psp_vdata pspv2 = {
.feature_reg = 0x109fc,
.inten_reg = 0x10690,
.intsts_reg = 0x10694,
+#ifdef CONFIG_HYGON_PSP2CPU_CMD
+ .p2c_cmdresp_reg = 0x109e8,
+ .p2c_cmdbuff_addr_lo_reg = 0x109ec,
+ .p2c_cmdbuff_addr_hi_reg = 0x109f0,
+#endif
};
#endif
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
index 6a5854ac5e93..9524b7e302e5 100644
--- a/include/linux/psp-sev.h
+++ b/include/linux/psp-sev.h
@@ -485,6 +485,16 @@ struct sev_data_dbg {
u32 len; /* In */
} __packed;
+#ifdef CONFIG_HYGON_PSP2CPU_CMD
+
+typedef int (*p2c_notifier_t)(uint32_t id, uint64_t data);
+
+int psp_register_cmd_notifier(uint32_t cmd_id, int (*notifier)(uint32_t id, uint64_t
data));
+
+int psp_unregister_cmd_notifier(uint32_t cmd_id, int (*notifier)(uint32_t id, uint64_t
data));
+
+#endif
+
#ifdef CONFIG_CRYPTO_DEV_SP_PSP
int psp_do_cmd(int cmd, void *data, int *psp_ret);
--
2.17.1