Commit d0b3a770 authored by chench00's avatar chench00
Browse files

crypto: command co-processor: Add another mailbox interrupt support for PSP sending command to X86

hygon inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I9B9XS


CVE: NA

---------------------------

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: default avatarchench <chench@hygon.cn>
parent 680134f1
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -53,3 +53,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
+119 −0
Original line number Diff line number Diff line
@@ -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 */
+8 −0
Original line number Diff line number Diff line
@@ -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__ */
+10 −1
Original line number Diff line number Diff line
@@ -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
+5 −1
Original line number Diff line number Diff line
@@ -187,7 +187,11 @@ int psp_dev_init(struct sp_device *sp)
	iowrite32(-1, psp->io_regs + psp->vdata->intsts_reg);

	/* Request an irq */
	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;
Loading