Commit fe08767e authored by xiongmengbiao's avatar xiongmengbiao
Browse files

drivers/crypto/ccp: concurrent psp access support between user and kernel space

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


CVE: NA

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

Add a self-defined mutex to support concurrent psp access between kernel
space and user space.

Signed-off-by: default avatarxiongmengbiao <xiongmengbiao@hygon.cn>
parent 5a996077
Loading
Loading
Loading
Loading
+26 −5
Original line number Diff line number Diff line
@@ -184,7 +184,13 @@ static long csv_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
	if (input.cmd > CSV_MAX)
		return -EINVAL;

	if (is_vendor_hygon()) {
		if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
					   PSP_MUTEX_TIMEOUT) != 1)
			return -EBUSY;
	} else {
		mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
	}

	switch (input.cmd) {
	case CSV_HGSC_CERT_IMPORT:
@@ -206,6 +212,9 @@ static long csv_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
		 * Release the mutex before calling the native ioctl function
		 * because it will acquires the mutex.
		 */
		if (is_vendor_hygon())
			psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
		else
			mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);
		return hygon_psp_hooks.sev_ioctl(file, ioctl, arg);
	}
@@ -213,6 +222,9 @@ static long csv_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
	if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd)))
		ret = -EFAULT;

	if (is_vendor_hygon())
		psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
	else
		mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);

	return ret;
@@ -372,7 +384,13 @@ static int csv_do_ringbuf_cmds(int *psp_ret)
	if (!hygon_psp_hooks.sev_dev_hooks_installed)
		return -ENODEV;

	if (is_vendor_hygon()) {
		if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
					   PSP_MUTEX_TIMEOUT) != 1)
			return -EBUSY;
	} else {
		mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
	}

	rc = __csv_ring_buffer_enter_locked(psp_ret);
	if (rc)
@@ -385,6 +403,9 @@ static int csv_do_ringbuf_cmds(int *psp_ret)
	csv_comm_mode = CSV_COMM_MAILBOX_ON;

cmd_unlock:
	if (is_vendor_hygon())
		psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
	else
		mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);

	return rc;
+174 −2
Original line number Diff line number Diff line
@@ -20,6 +20,169 @@
/* Function and variable pointers for hooks */
struct hygon_psp_hooks_table hygon_psp_hooks;

static struct psp_misc_dev *psp_misc;

uint64_t atomic64_exchange(uint64_t *dst, uint64_t val)
{
	return xchg(dst, val);
}

int psp_mutex_init(struct psp_mutex *mutex)
{
	if (!mutex)
		return -1;
	mutex->locked = 0;
	return 0;
}

int psp_mutex_trylock(struct psp_mutex *mutex)
{
	if (atomic64_exchange(&mutex->locked, 1))
		return 0;
	else
		return 1;
}

int psp_mutex_lock_timeout(struct psp_mutex *mutex, uint64_t ms)
{
	int ret = 0;
	unsigned long je;

	je = jiffies + msecs_to_jiffies(ms);
	do {
		if (psp_mutex_trylock(mutex)) {
			ret = 1;
			break;
		}
	} while (time_before(jiffies, je));

	return ret;
}
EXPORT_SYMBOL_GPL(psp_mutex_lock_timeout);

int psp_mutex_unlock(struct psp_mutex *mutex)
{
	if (!mutex)
		return -1;

	atomic64_exchange(&mutex->locked, 0);
	return 0;
}
EXPORT_SYMBOL_GPL(psp_mutex_unlock);

static int mmap_psp(struct file *filp, struct vm_area_struct *vma)
{
	unsigned long page;

	page = virt_to_phys((void *)psp_misc->data_pg_aligned) >> PAGE_SHIFT;

	if (remap_pfn_range(vma, vma->vm_start, page, (vma->vm_end - vma->vm_start),
				vma->vm_page_prot)) {
		pr_info("remap failed...");
		return -1;
	}
	vm_flags_mod(vma, VM_DONTDUMP|VM_DONTEXPAND, 0);
	pr_info("remap_pfn_rang page:[%lu] ok.\n", page);
	return 0;
}

static ssize_t read_psp(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	ssize_t remaining;

	if ((*ppos + count) > PAGE_SIZE) {
		pr_info("%s: invalid address range, pos %llx, count %lx\n",
			__func__, *ppos, count);
		return -EFAULT;
	}

	remaining = copy_to_user(buf, (char *)psp_misc->data_pg_aligned + *ppos, count);
	if (remaining)
		return -EFAULT;
	*ppos += count;

	return count;
}

static ssize_t write_psp(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	ssize_t remaining, written;

	if ((*ppos + count) > PAGE_SIZE) {
		pr_info("%s: invalid address range, pos %llx, count %lx\n",
			__func__, *ppos, count);
		return -EFAULT;
	}

	remaining = copy_from_user((char *)psp_misc->data_pg_aligned + *ppos, buf, count);
	written = count - remaining;
	if (!written)
		return -EFAULT;

	*ppos += written;

	return written;
}

static const struct file_operations psp_fops = {
	.owner          = THIS_MODULE,
	.mmap		= mmap_psp,
	.read		= read_psp,
	.write		= write_psp,
};

int hygon_psp_additional_setup(struct sp_device *sp)
{
	struct device *dev = sp->dev;
	int ret = 0;

	if (!psp_misc) {
		struct miscdevice *misc;

		psp_misc = devm_kzalloc(dev, sizeof(*psp_misc), GFP_KERNEL);
		if (!psp_misc)
			return -ENOMEM;
		psp_misc->data_pg_aligned = (struct psp_dev_data *)get_zeroed_page(GFP_KERNEL);
		if (!psp_misc->data_pg_aligned) {
			dev_err(dev, "alloc psp data page failed\n");
			devm_kfree(dev, psp_misc);
			psp_misc = NULL;
			return -ENOMEM;
		}
		SetPageReserved(virt_to_page(psp_misc->data_pg_aligned));
		psp_mutex_init(&psp_misc->data_pg_aligned->mb_mutex);

		*(uint32_t *)((void *)psp_misc->data_pg_aligned + 8) = 0xdeadbeef;
		misc = &psp_misc->misc;
		misc->minor = MISC_DYNAMIC_MINOR;
		misc->name = "hygon_psp_config";
		misc->fops = &psp_fops;

		ret = misc_register(misc);
		if (ret)
			return ret;
		kref_init(&psp_misc->refcount);
		hygon_psp_hooks.psp_misc = psp_misc;
	} else {
		kref_get(&psp_misc->refcount);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(hygon_psp_additional_setup);

void hygon_psp_exit(struct kref *ref)
{
	struct psp_misc_dev *misc_dev = container_of(ref, struct psp_misc_dev, refcount);

	misc_deregister(&misc_dev->misc);
	ClearPageReserved(virt_to_page(misc_dev->data_pg_aligned));
	free_page((unsigned long)misc_dev->data_pg_aligned);
	psp_misc = NULL;
	hygon_psp_hooks.psp_misc = NULL;
}
EXPORT_SYMBOL_GPL(hygon_psp_exit);

int fixup_hygon_psp_caps(struct psp_device *psp)
{
	/* the hygon psp is unavailable if bit0 is cleared in feature reg */
@@ -96,9 +259,18 @@ static int __psp_do_cmd_locked(int cmd, void *data, int *psp_ret)
int psp_do_cmd(int cmd, void *data, int *psp_ret)
{
	int rc;

	if (is_vendor_hygon()) {
		if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
					   PSP_MUTEX_TIMEOUT) != 1)
			return -EBUSY;
	} else {
		mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
	}

	rc = __psp_do_cmd_locked(cmd, data, psp_ret);
	if (is_vendor_hygon())
		psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
	else
		mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);

	return rc;
+21 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@

#include <linux/mutex.h>
#include <linux/bits.h>
#include <linux/miscdevice.h>

#include "sp-dev.h"

@@ -30,6 +31,7 @@
extern struct hygon_psp_hooks_table {
	bool sev_dev_hooks_installed;
	struct mutex *sev_cmd_mutex;
	struct psp_misc_dev *psp_misc;
	bool *psp_dead;
	int *psp_timeout;
	int *psp_cmd_timeout;
@@ -42,6 +44,25 @@ extern struct hygon_psp_hooks_table {
	long (*sev_ioctl)(struct file *file, unsigned int ioctl, unsigned long arg);
} hygon_psp_hooks;

#define PSP_MUTEX_TIMEOUT 10000
struct psp_mutex {
	uint64_t locked;
};

struct psp_dev_data {
	struct psp_mutex mb_mutex;
};

struct psp_misc_dev {
	struct kref refcount;
	struct psp_dev_data *data_pg_aligned;
	struct miscdevice misc;
};

int hygon_psp_additional_setup(struct sp_device *sp);
void hygon_psp_exit(struct kref *ref);
int psp_mutex_lock_timeout(struct psp_mutex *mutex, uint64_t ms);
int psp_mutex_unlock(struct psp_mutex *mutex);
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);
+8 −0
Original line number Diff line number Diff line
@@ -199,6 +199,11 @@ int psp_dev_init(struct sp_device *sp)

	/* Request an irq */
	if (is_vendor_hygon()) {
		ret = hygon_psp_additional_setup(sp);
		if (ret) {
			dev_err(dev, "psp: unable to do additional setup\n");
			goto e_err;
		}
		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);
@@ -263,6 +268,9 @@ void psp_dev_destroy(struct sp_device *sp)

	sp_free_psp_irq(sp, psp);

	if (is_vendor_hygon() && hygon_psp_hooks.psp_misc)
		kref_put(&hygon_psp_hooks.psp_misc->refcount, hygon_psp_exit);

	if (sp->clear_psp_master_device)
		sp->clear_psp_master_device(sp);
}
+44 −8
Original line number Diff line number Diff line
@@ -409,9 +409,18 @@ static int sev_do_cmd(int cmd, void *data, int *psp_ret)
{
	int rc;

	mutex_lock(&sev_cmd_mutex);
	if (is_vendor_hygon()) {
		if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
					   PSP_MUTEX_TIMEOUT) != 1)
			return -EBUSY;
	} else {
		mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
	}
	rc = __sev_do_cmd_locked(cmd, data, psp_ret);
	mutex_unlock(&sev_cmd_mutex);
	if (is_vendor_hygon())
		psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
	else
		mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);

	return rc;
}
@@ -530,9 +539,18 @@ int sev_platform_init(int *error)
{
	int rc;

	mutex_lock(&sev_cmd_mutex);
	if (is_vendor_hygon()) {
		if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
					   PSP_MUTEX_TIMEOUT) != 1)
			return -EBUSY;
	} else {
		mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
	}
	rc = __sev_platform_init_locked(error);
	mutex_unlock(&sev_cmd_mutex);
	if (is_vendor_hygon())
		psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
	else
		mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);

	return rc;
}
@@ -570,9 +588,18 @@ static int sev_platform_shutdown(int *error)
{
	int rc;

	mutex_lock(&sev_cmd_mutex);
	if (is_vendor_hygon()) {
		if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
					   PSP_MUTEX_TIMEOUT) != 1)
			return -EBUSY;
	} else {
		mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
	}
	rc = __sev_platform_shutdown_locked(NULL);
	mutex_unlock(&sev_cmd_mutex);
	if (is_vendor_hygon())
		psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
	else
		mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);

	return rc;
}
@@ -1132,7 +1159,13 @@ static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
	if (input.cmd > SEV_MAX)
		return -EINVAL;

	mutex_lock(&sev_cmd_mutex);
	if (is_vendor_hygon()) {
		if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
					   PSP_MUTEX_TIMEOUT) != 1)
			return -EBUSY;
	} else {
		mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
	}

	switch (input.cmd) {

@@ -1172,7 +1205,10 @@ static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
	if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd)))
		ret = -EFAULT;
out:
	mutex_unlock(&sev_cmd_mutex);
	if (is_vendor_hygon())
		psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
	else
		mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);

	return ret;
}