Commit 7ccc4f4e authored by Mario Limonciello's avatar Mario Limonciello Committed by Herbert Xu
Browse files

crypto: ccp - Add support for an interface for platform features



Some platforms with a PSP support an interface for features that
interact directly with the PSP instead of through a SEV or TEE
environment.

Initialize this interface so that other drivers can consume it.
These drivers may either be subdrivers for the ccp module or
external modules.  For external modules, export a symbol for them
to utilize.

Acked-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarMario Limonciello <mario.limonciello@amd.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 1c5c1daf
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -10,7 +10,8 @@ ccp-$(CONFIG_CRYPTO_DEV_CCP_DEBUGFS) += ccp-debugfs.o
ccp-$(CONFIG_PCI) += sp-pci.o
ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o \
                                   sev-dev.o \
                                   tee-dev.o
                                   tee-dev.o \
                                   platform-access.o

obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o
ccp-crypto-objs := ccp-crypto-main.o \
+166 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * AMD Platform Security Processor (PSP) Platform Access interface
 *
 * Copyright (C) 2023 Advanced Micro Devices, Inc.
 *
 * Author: Mario Limonciello <mario.limonciello@amd.com>
 *
 * Some of this code is adapted from drivers/i2c/busses/i2c-designware-amdpsp.c
 * developed by Jan Dabros <jsd@semihalf.com> and Copyright (C) 2022 Google Inc.
 *
 */

#include <linux/bitfield.h>
#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/mutex.h>

#include "platform-access.h"

#define PSP_CMD_TIMEOUT_US	(500 * USEC_PER_MSEC)

/* Recovery field should be equal 0 to start sending commands */
static int check_recovery(u32 __iomem *cmd)
{
	return FIELD_GET(PSP_CMDRESP_RECOVERY, ioread32(cmd));
}

static int wait_cmd(u32 __iomem *cmd)
{
	u32 tmp, expected;

	/* Expect mbox_cmd to be cleared and ready bit to be set by PSP */
	expected = FIELD_PREP(PSP_CMDRESP_RESP, 1);

	/*
	 * Check for readiness of PSP mailbox in a tight loop in order to
	 * process further as soon as command was consumed.
	 */
	return readl_poll_timeout(cmd, tmp, (tmp & expected), 0,
				  PSP_CMD_TIMEOUT_US);
}

int psp_check_platform_access_status(void)
{
	struct psp_device *psp = psp_get_master_device();

	if (!psp || !psp->platform_access_data)
		return -ENODEV;

	return 0;
}
EXPORT_SYMBOL(psp_check_platform_access_status);

int psp_send_platform_access_msg(enum psp_platform_access_msg msg,
				 struct psp_request *req)
{
	struct psp_device *psp = psp_get_master_device();
	u32 __iomem *cmd, *lo, *hi;
	struct psp_platform_access_device *pa_dev;
	phys_addr_t req_addr;
	u32 cmd_reg;
	int ret;

	if (!psp || !psp->platform_access_data)
		return -ENODEV;

	pa_dev = psp->platform_access_data;
	cmd = psp->io_regs + pa_dev->vdata->cmdresp_reg;
	lo = psp->io_regs + pa_dev->vdata->cmdbuff_addr_lo_reg;
	hi = psp->io_regs + pa_dev->vdata->cmdbuff_addr_hi_reg;

	mutex_lock(&pa_dev->mailbox_mutex);

	if (check_recovery(cmd)) {
		dev_dbg(psp->dev, "platform mailbox is in recovery\n");
		ret = -EBUSY;
		goto unlock;
	}

	if (wait_cmd(cmd)) {
		dev_dbg(psp->dev, "platform mailbox is not done processing command\n");
		ret = -EBUSY;
		goto unlock;
	}

	/*
	 * Fill mailbox with address of command-response buffer, which will be
	 * used for sending i2c requests as well as reading status returned by
	 * PSP. Use physical address of buffer, since PSP will map this region.
	 */
	req_addr = __psp_pa(req);
	iowrite32(lower_32_bits(req_addr), lo);
	iowrite32(upper_32_bits(req_addr), hi);

	print_hex_dump_debug("->psp ", DUMP_PREFIX_OFFSET, 16, 2, req,
			     req->header.payload_size, false);

	/* Write command register to trigger processing */
	cmd_reg = FIELD_PREP(PSP_CMDRESP_CMD, msg);
	iowrite32(cmd_reg, cmd);

	if (wait_cmd(cmd)) {
		ret = -ETIMEDOUT;
		goto unlock;
	}

	/* Ensure it was triggered by this driver */
	if (ioread32(lo) != lower_32_bits(req_addr) ||
	    ioread32(hi) != upper_32_bits(req_addr)) {
		ret = -EBUSY;
		goto unlock;
	}

	/* Store the status in request header for caller to investigate */
	cmd_reg = ioread32(cmd);
	req->header.status = FIELD_GET(PSP_CMDRESP_STS, cmd_reg);
	if (req->header.status) {
		ret = -EIO;
		goto unlock;
	}

	print_hex_dump_debug("<-psp ", DUMP_PREFIX_OFFSET, 16, 2, req,
			     req->header.payload_size, false);

	ret = 0;

unlock:
	mutex_unlock(&pa_dev->mailbox_mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(psp_send_platform_access_msg);

void platform_access_dev_destroy(struct psp_device *psp)
{
	struct psp_platform_access_device *pa_dev = psp->platform_access_data;

	if (!pa_dev)
		return;

	mutex_destroy(&pa_dev->mailbox_mutex);
	psp->platform_access_data = NULL;
}

int platform_access_dev_init(struct psp_device *psp)
{
	struct device *dev = psp->dev;
	struct psp_platform_access_device *pa_dev;

	pa_dev = devm_kzalloc(dev, sizeof(*pa_dev), GFP_KERNEL);
	if (!pa_dev)
		return -ENOMEM;

	psp->platform_access_data = pa_dev;
	pa_dev->psp = psp;
	pa_dev->dev = dev;

	pa_dev->vdata = (struct platform_access_vdata *)psp->vdata->platform_access;

	mutex_init(&pa_dev->mailbox_mutex);

	dev_dbg(dev, "platform access enabled\n");

	return 0;
}
+34 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * AMD Platform Security Processor (PSP) Platform Access interface
 *
 * Copyright (C) 2023 Advanced Micro Devices, Inc.
 *
 * Author: Mario Limonciello <mario.limonciello@amd.com>
 */

#ifndef __PSP_PLATFORM_ACCESS_H__
#define __PSP_PLATFORM_ACCESS_H__

#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/psp-platform-access.h>

#include "psp-dev.h"

struct psp_platform_access_device {
	struct device *dev;
	struct psp_device *psp;

	struct platform_access_vdata *vdata;

	struct mutex mailbox_mutex;

	void *platform_access_data;
};

void platform_access_dev_destroy(struct psp_device *psp);
int platform_access_dev_init(struct psp_device *psp);

#endif /* __PSP_PLATFORM_ACCESS_H__ */
+17 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include "psp-dev.h"
#include "sev-dev.h"
#include "tee-dev.h"
#include "platform-access.h"

struct psp_device *psp_master;

@@ -102,6 +103,17 @@ static int psp_check_tee_support(struct psp_device *psp)
	return 0;
}

static void psp_init_platform_access(struct psp_device *psp)
{
	int ret;

	ret = platform_access_dev_init(psp);
	if (ret) {
		dev_warn(psp->dev, "platform access init failed: %d\n", ret);
		return;
	}
}

static int psp_init(struct psp_device *psp)
{
	int ret;
@@ -118,6 +130,9 @@ static int psp_init(struct psp_device *psp)
			return ret;
	}

	if (psp->vdata->platform_access)
		psp_init_platform_access(psp);

	return 0;
}

@@ -198,6 +213,8 @@ void psp_dev_destroy(struct sp_device *sp)

	tee_dev_destroy(psp);

	platform_access_dev_destroy(psp);

	sp_free_psp_irq(sp, psp);

	if (sp->clear_psp_master_device)
+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ struct psp_device {

	void *sev_data;
	void *tee_data;
	void *platform_access_data;

	unsigned int capability;
};
Loading