crypto: ccp: Add another mailbox interrupt support for PSP sending command to X86

Upstream: no

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.

Currently, Hygon PSP does not support bootloader info reg, remove
the value of bootloader_info_reg.

Signed-off-by: chench <chench@hygon.cn>
Signed-off-by: hanliyang <hanliyang@hygon.cn>
This commit is contained in:
chench 2024-03-18 11:55:07 +08:00 committed by hanliyang
parent f94ca9e54f
commit 88a4c829d3
7 changed files with 168 additions and 2 deletions

View File

@ -60,3 +60,10 @@ config CRYPTO_DEV_CCP_DEBUGFS
help
Expose CCP device information such as operation statistics, feature
information, and descriptor queue contents.
config HYGON_PSP2CPU_CMD
bool "Hygon PSP2CPU Command Interface"
default y
depends on CRYPTO_DEV_SP_PSP
help
Hygon PSP2CPU Command Support

View File

@ -104,3 +104,122 @@ int psp_do_cmd(int cmd, void *data, int *psp_ret)
return rc;
}
EXPORT_SYMBOL_GPL(psp_do_cmd);
#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, p2c_notifier_t notifier)
{
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, p2c_notifier_t notifier)
{
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;
struct sev_device *sev = psp->sev_irq_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 & SEV_CMD_COMPLETE) {
/* Check if it is SEV command completion: */
reg = ioread32(psp->io_regs + psp->vdata->sev->cmdresp_reg);
if (reg & PSP_CMDRESP_RESP) {
sev->int_rcvd = 1;
wake_up(&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 & SEV_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;
}
int sp_request_hygon_psp_irq(struct sp_device *sp, irq_handler_t handler,
const char *name, void *data)
{
return sp_request_psp_irq(sp, psp_irq_handler_hygon, name, data);
}
#else /* !CONFIG_HYGON_PSP2CPU_CMD */
int sp_request_hygon_psp_irq(struct sp_device *sp, irq_handler_t handler,
const char *name, void *data)
{
return sp_request_psp_irq(sp, handler, name, data);
}
#endif /* CONFIG_HYGON_PSP2CPU_CMD */

View File

@ -11,12 +11,18 @@
#define __CCP_HYGON_PSP_DEV_H__
#include <linux/mutex.h>
#include <linux/bits.h>
#include "sp-dev.h"
#include "../psp-dev.h"
#include "../sev-dev.h"
#ifdef CONFIG_HYGON_PSP2CPU_CMD
#define PSP_X86_CMD BIT(2)
#define P2C_NOTIFIERS_MAX 16
#endif
/*
* Hooks table: a table of function and variable pointers filled in
* when psp init.
@ -37,5 +43,7 @@ extern struct hygon_psp_hooks_table {
} hygon_psp_hooks;
int fixup_hygon_psp_caps(struct psp_device *psp);
int sp_request_hygon_psp_irq(struct sp_device *sp, irq_handler_t handler,
const char *name, void *data);
#endif /* __CCP_HYGON_PSP_DEV_H__ */

View File

@ -22,10 +22,14 @@ static const struct sev_vdata csvv1 = {
static const struct psp_vdata pspv1 = {
.sev = &csvv1,
.bootloader_info_reg = 0x105ec, /* C2PMSG_59 */
.feature_reg = 0x105fc, /* C2PMSG_63 */
.inten_reg = 0x10610, /* P2CMSG_INTEN */
.intsts_reg = 0x10614, /* P2CMSG_INTSTS */
#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 = {
@ -33,6 +37,11 @@ static const struct psp_vdata pspv2 = {
.feature_reg = 0x105fc,
.inten_reg = 0x10670,
.intsts_reg = 0x10674,
#ifdef CONFIG_HYGON_PSP2CPU_CMD
.p2c_cmdresp_reg = 0x105e8,
.p2c_cmdbuff_addr_lo_reg = 0x105ec,
.p2c_cmdbuff_addr_hi_reg = 0x105f0,
#endif
};
#endif

View File

@ -187,7 +187,11 @@ 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 (is_vendor_hygon()) {
ret = sp_request_hygon_psp_irq(psp->sp, psp_irq_handler, psp->name, psp);
} 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;

View File

@ -76,6 +76,11 @@ struct psp_vdata {
const unsigned int intsts_reg;
const unsigned int bootloader_info_reg;
const unsigned int platform_features;
#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 */

View File

@ -156,4 +156,18 @@ csv_issue_ringbuf_cmds_external_user(struct file *filep, int *psp_ret) { return
#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
typedef int (*p2c_notifier_t)(uint32_t id, uint64_t data);
#ifdef CONFIG_HYGON_PSP2CPU_CMD
int psp_register_cmd_notifier(uint32_t cmd_id, p2c_notifier_t notifier);
int psp_unregister_cmd_notifier(uint32_t cmd_id, p2c_notifier_t notifier);
#else /* !CONFIG_HYGON_PSP2CPU_CMD */
int psp_register_cmd_notifier(uint32_t cmd_id, p2c_notifier_t notifier) { return -ENODEV; }
int psp_unregister_cmd_notifier(uint32_t cmd_id, p2c_notifier_t notifier) { return -ENODEV; }
#endif /* CONFIG_HYGON_PSP2CPU_CMD */
#endif /* __PSP_HYGON_H__ */