Unverified Commit 5aea5388 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!11157 [OLK-6.6] Support Hygon Trusted Key Management run on CSV Guest

Merge Pull Request from: @xisme 
 
Support Hygon Trusted Key Management run on CSV Guest

issue: [【OLK-6.6】Support Hygon Trusted Key Management run on CSV Guest](https://gitee.com/openeuler/kernel/issues/IAMHXY)

Based on the existing virtualization function, support for CSV virtual machine to send encrypted TKM commands to PSP hardware is added 
 
Link:https://gitee.com/openeuler/kernel/pulls/11157

 

Reviewed-by: default avatarZhang Peng <zhangpeng362@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 497bb858 711ef0c7
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -5215,13 +5215,22 @@ static int kvm_hygon_arch_hypercall(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64
	struct kvm_vpsp vpsp = {
		.kvm = kvm,
		.write_guest = kvm_write_guest,
		.read_guest = kvm_read_guest
		.read_guest = kvm_read_guest,
		.gfn_to_pfn = gfn_to_pfn,
	};

	if (sev_guest(kvm)) {
		vpsp.vm_handle = to_kvm_svm(kvm)->sev_info.handle;
		vpsp.is_csv_guest = 1;
	}

	switch (nr) {
	case KVM_HC_PSP_OP:
		ret = kvm_pv_psp_op(&vpsp, a0, a1, a2, a3);
	case KVM_HC_PSP_COPY_FORWARD_OP:
		ret = kvm_pv_psp_copy_forward_op(&vpsp, a0, a1, a2);
		break;
	case KVM_HC_PSP_FORWARD_OP:
		ret = kvm_pv_psp_forward_op(&vpsp, a0, a1, a2);
		break;

	default:
		ret = -KVM_ENOSYS;
		break;
+7 −2
Original line number Diff line number Diff line
@@ -9972,7 +9972,10 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
	}

	if (static_call(kvm_x86_get_cpl)(vcpu) != 0 &&
	    !(is_x86_vendor_hygon() && (nr == KVM_HC_VM_ATTESTATION || nr == KVM_HC_PSP_OP))) {
	    !(is_x86_vendor_hygon() && (nr == KVM_HC_VM_ATTESTATION
					|| nr == KVM_HC_PSP_OP_OBSOLETE
					|| nr == KVM_HC_PSP_COPY_FORWARD_OP
					|| nr == KVM_HC_PSP_FORWARD_OP))) {
		ret = -KVM_EPERM;
		goto out;
	}
@@ -10009,7 +10012,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
		kvm_sched_yield(vcpu, a0);
		ret = 0;
		break;
	case KVM_HC_PSP_OP:
	case KVM_HC_PSP_OP_OBSOLETE:
	case KVM_HC_PSP_COPY_FORWARD_OP:
	case KVM_HC_PSP_FORWARD_OP:
		ret = -KVM_ENOSYS;
		if (kvm_arch_hypercall)
			ret = kvm_arch_hypercall(vcpu->kvm, nr, a0, a1, a2, a3);
+90 −11
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/psp.h>
#include <linux/psp-hygon.h>
#include <uapi/linux/psp-hygon.h>
#include <linux/bitfield.h>

#include "psp-dev.h"
#include "csv-dev.h"
@@ -645,12 +646,12 @@ static int vpsp_dequeue_cmd(int prio, int index,
 * Populate the command from the virtual machine to the queue to
 * support execution in ringbuffer mode
 */
static int vpsp_fill_cmd_queue(uint32_t vid, int prio, int cmd, void *data, uint16_t flags)
static int vpsp_fill_cmd_queue(int prio, int cmd, phys_addr_t phy_addr, uint16_t flags)
{
	struct csv_cmdptr_entry cmdptr = { };
	int index = -1;

	cmdptr.cmd_buf_ptr = PUT_PSP_VID(__psp_pa(data), vid);
	cmdptr.cmd_buf_ptr = phy_addr;
	cmdptr.cmd_id = cmd;
	cmdptr.cmd_flags = flags;

@@ -938,12 +939,91 @@ static int vpsp_rb_check_and_cmd_prio_parse(uint8_t *prio,
	return rb_supported;
}

int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret);
static int __vpsp_do_cmd_locked(int cmd, phys_addr_t phy_addr, int *psp_ret)
{
	struct psp_device *psp = psp_master;
	struct sev_device *sev;
	unsigned int phys_lsb, phys_msb;
	unsigned int reg, ret = 0;

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

	if (*hygon_psp_hooks.psp_dead)
		return -EBUSY;

	sev = psp->sev_data;

	/* Get the physical address of the command buffer */
	phys_lsb = phy_addr ? lower_32_bits(phy_addr) : 0;
	phys_msb = phy_addr ? upper_32_bits(phy_addr) : 0;

	dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n",
		cmd, phys_msb, phys_lsb, *hygon_psp_hooks.psp_timeout);

	iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg);
	iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg);

	sev->int_rcvd = 0;

	reg = FIELD_PREP(SEV_CMDRESP_CMD, cmd) | SEV_CMDRESP_IOC;
	iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg);

	/* wait for command completion */
	ret = hygon_psp_hooks.sev_wait_cmd_ioc(sev, &reg, *hygon_psp_hooks.psp_timeout);
	if (ret) {
		if (psp_ret)
			*psp_ret = 0;

		dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd);
		*hygon_psp_hooks.psp_dead = true;

		return ret;
	}

	*hygon_psp_hooks.psp_timeout = *hygon_psp_hooks.psp_cmd_timeout;

	if (psp_ret)
		*psp_ret = FIELD_GET(PSP_CMDRESP_STS, reg);

	if (FIELD_GET(PSP_CMDRESP_STS, reg)) {
		dev_dbg(sev->dev, "sev command %#x failed (%#010lx)\n",
			cmd, FIELD_GET(PSP_CMDRESP_STS, reg));
		ret = -EIO;
	}

	return ret;
}

int vpsp_do_cmd(int cmd, phys_addr_t phy_addr, int *psp_ret)
{
	int rc;
	int mutex_enabled = READ_ONCE(hygon_psp_hooks.psp_mutex_enabled);

	if (is_vendor_hygon() && mutex_enabled) {
		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 = __vpsp_do_cmd_locked(cmd, phy_addr, psp_ret);

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

	return rc;
}

/*
 * Try to obtain the result again by the command index, this
 * interface is used in ringbuffer mode
 */
int vpsp_try_get_result(uint32_t vid, uint8_t prio, uint32_t index, void *data,
int vpsp_try_get_result(uint8_t prio, uint32_t index, phys_addr_t phy_addr,
		struct vpsp_ret *psp_ret)
{
	int ret = 0;
@@ -963,8 +1043,7 @@ int vpsp_try_get_result(uint32_t vid, uint8_t prio, uint32_t index, void *data,
			/* dequeue command from queue*/
			vpsp_dequeue_cmd(prio, index, &cmd);

			ret = __vpsp_do_cmd_locked(vid, cmd.cmd_id, data,
					(int *)psp_ret);
			ret = __vpsp_do_cmd_locked(cmd.cmd_id, phy_addr, (int *)psp_ret);
			psp_ret->status = VPSP_FINISH;
			vpsp_psp_mutex_unlock();
			if (unlikely(ret)) {
@@ -1007,7 +1086,7 @@ EXPORT_SYMBOL_GPL(vpsp_try_get_result);
 * vpsp_try_get_result interface will be used to obtain the result
 * later again
 */
int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret)
int vpsp_try_do_cmd(int cmd, phys_addr_t phy_addr, struct vpsp_ret *psp_ret)
{
	int ret = 0;
	int rb_supported;
@@ -1019,10 +1098,10 @@ int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret)
			(struct vpsp_cmd *)&cmd);
	if (rb_supported) {
		/* fill command in ringbuffer's queue and get index */
		index = vpsp_fill_cmd_queue(vid, prio, cmd, data, 0);
		index = vpsp_fill_cmd_queue(prio, cmd, phy_addr, 0);
		if (unlikely(index < 0)) {
			/* do mailbox command if queuing failed*/
			ret = vpsp_do_cmd(vid, cmd, data, (int *)psp_ret);
			ret = vpsp_do_cmd(cmd, phy_addr, (int *)psp_ret);
			if (unlikely(ret)) {
				if (ret == -EIO) {
					ret = 0;
@@ -1038,14 +1117,14 @@ int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret)
		}

		/* try to get result from the ringbuffer command */
		ret = vpsp_try_get_result(vid, prio, index, data, psp_ret);
		ret = vpsp_try_get_result(prio, index, phy_addr, psp_ret);
		if (unlikely(ret)) {
			pr_err("[%s]: vpsp_try_get_result failed %d\n", __func__, ret);
			goto end;
		}
	} else {
		/* mailbox mode */
		ret = vpsp_do_cmd(vid, cmd, data, (int *)psp_ret);
		ret = vpsp_do_cmd(cmd, phy_addr, (int *)psp_ret);
		if (unlikely(ret)) {
			if (ret == -EIO) {
				ret = 0;
+127 −121
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ enum HYGON_PSP_OPCODE {
	HYGON_PSP_MUTEX_ENABLE = 1,
	HYGON_PSP_MUTEX_DISABLE,
	HYGON_VPSP_CTRL_OPT,
	HYGON_PSP_OP_PIN_USER_PAGE,
	HYGON_PSP_OP_UNPIN_USER_PAGE,
	HYGON_PSP_OPCODE_MAX_NR,
};

@@ -38,16 +40,26 @@ enum VPSP_DEV_CTRL_OPCODE {
	VPSP_OP_VID_DEL,
	VPSP_OP_SET_DEFAULT_VID_PERMISSION,
	VPSP_OP_GET_DEFAULT_VID_PERMISSION,
	VPSP_OP_SET_GPA,
};

struct vpsp_dev_ctrl {
	unsigned char op;
	/**
	 * To be compatible with old user mode,
	 * struct vpsp_dev_ctrl must be kept at 132 bytes.
	 */
	unsigned char resv[3];
	union {
		unsigned int vid;
		// Set or check the permissions for the default VID
		unsigned int def_vid_perm;
		struct {
			u64 gpa_start;
			u64 gpa_end;
		} gpa;
		unsigned char reserved[128];
	} data;
	} __packed data;
};

uint64_t atomic64_exchange(uint64_t *dst, uint64_t val)
@@ -160,19 +172,15 @@ DEFINE_RWLOCK(vpsp_rwlock);
#define VPSP_VID_MAX_ENTRIES    2048
#define VPSP_VID_NUM_MAX        64

struct vpsp_vid_entry {
	uint32_t vid;
	pid_t pid;
};
static struct vpsp_vid_entry g_vpsp_vid_array[VPSP_VID_MAX_ENTRIES];
static struct vpsp_context g_vpsp_context_array[VPSP_VID_MAX_ENTRIES];
static uint32_t g_vpsp_vid_num;
static int compare_vid_entries(const void *a, const void *b)
{
	return ((struct vpsp_vid_entry *)a)->pid - ((struct vpsp_vid_entry *)b)->pid;
	return ((struct vpsp_context *)a)->pid - ((struct vpsp_context *)b)->pid;
}
static void swap_vid_entries(void *a, void *b, int size)
{
	struct vpsp_vid_entry entry;
	struct vpsp_context entry;

	memcpy(&entry, a, size);
	memcpy(a, b, size);
@@ -197,43 +205,41 @@ int vpsp_get_default_vid_permission(void)
EXPORT_SYMBOL_GPL(vpsp_get_default_vid_permission);

/**
 * When the virtual machine executes the 'tkm' command,
 * it needs to retrieve the corresponding 'vid'
 * by performing a binary search using 'kvm->userspace_pid'.
 * get a vpsp context from pid
 */
int vpsp_get_vid(uint32_t *vid, pid_t pid)
int vpsp_get_context(struct vpsp_context **ctx, pid_t pid)
{
	struct vpsp_vid_entry new_entry = {.pid = pid};
	struct vpsp_vid_entry *existing_entry = NULL;
	struct vpsp_context new_entry = {.pid = pid};
	struct vpsp_context *existing_entry = NULL;

	read_lock(&vpsp_rwlock);
	existing_entry = bsearch(&new_entry, g_vpsp_vid_array, g_vpsp_vid_num,
				sizeof(struct vpsp_vid_entry), compare_vid_entries);
	existing_entry = bsearch(&new_entry, g_vpsp_context_array, g_vpsp_vid_num,
				sizeof(struct vpsp_context), compare_vid_entries);
	read_unlock(&vpsp_rwlock);

	if (!existing_entry)
		return -ENOENT;
	if (vid) {
		*vid = existing_entry->vid;
		pr_debug("PSP: %s %d, by pid %d\n", __func__, *vid, pid);
	}

	if (ctx)
		*ctx = existing_entry;

	return 0;
}
EXPORT_SYMBOL_GPL(vpsp_get_vid);
EXPORT_SYMBOL_GPL(vpsp_get_context);

/**
 * Upon qemu startup, this section checks whether
 * the '-device psp,vid' parameter is specified.
 * If set, it utilizes the 'vpsp_add_vid' function
 * to insert the 'vid' and 'pid' values into the 'g_vpsp_vid_array'.
 * to insert the 'vid' and 'pid' values into the 'g_vpsp_context_array'.
 * The insertion is done in ascending order of 'pid'.
 */
static int vpsp_add_vid(uint32_t vid)
{
	pid_t cur_pid = task_pid_nr(current);
	struct vpsp_vid_entry new_entry = {.vid = vid, .pid = cur_pid};
	struct vpsp_context new_entry = {.vid = vid, .pid = cur_pid};

	if (vpsp_get_vid(NULL, cur_pid) == 0)
	if (vpsp_get_context(NULL, cur_pid) == 0)
		return -EEXIST;
	if (g_vpsp_vid_num == VPSP_VID_MAX_ENTRIES)
		return -ENOMEM;
@@ -241,8 +247,8 @@ static int vpsp_add_vid(uint32_t vid)
		return -EINVAL;

	write_lock(&vpsp_rwlock);
	memcpy(&g_vpsp_vid_array[g_vpsp_vid_num++], &new_entry, sizeof(struct vpsp_vid_entry));
	sort(g_vpsp_vid_array, g_vpsp_vid_num, sizeof(struct vpsp_vid_entry),
	memcpy(&g_vpsp_context_array[g_vpsp_vid_num++], &new_entry, sizeof(struct vpsp_context));
	sort(g_vpsp_context_array, g_vpsp_vid_num, sizeof(struct vpsp_context),
				compare_vid_entries, swap_vid_entries);
	pr_info("PSP: add vid %d, by pid %d, total vid num is %d\n", vid, cur_pid, g_vpsp_vid_num);
	write_unlock(&vpsp_rwlock);
@@ -261,12 +267,12 @@ static int vpsp_del_vid(void)

	write_lock(&vpsp_rwlock);
	for (i = 0; i < g_vpsp_vid_num; ++i) {
		if (g_vpsp_vid_array[i].pid == cur_pid) {
		if (g_vpsp_context_array[i].pid == cur_pid) {
			--g_vpsp_vid_num;
			pr_info("PSP: delete vid %d, by pid %d, total vid num is %d\n",
				g_vpsp_vid_array[i].vid, cur_pid, g_vpsp_vid_num);
			memmove(&g_vpsp_vid_array[i], &g_vpsp_vid_array[i + 1],
				sizeof(struct vpsp_vid_entry) * (g_vpsp_vid_num - i));
				g_vpsp_context_array[i].vid, cur_pid, g_vpsp_vid_num);
			memmove(&g_vpsp_context_array[i], &g_vpsp_context_array[i + 1],
				sizeof(struct vpsp_context) * (g_vpsp_vid_num - i));
			ret = 0;
			goto end;
		}
@@ -277,6 +283,85 @@ static int vpsp_del_vid(void)
	return ret;
}

static int vpsp_set_gpa_range(u64 gpa_start, u64 gpa_end)
{
	pid_t cur_pid = task_pid_nr(current);
	struct vpsp_context *ctx = NULL;

	vpsp_get_context(&ctx, cur_pid);
	if (!ctx) {
		pr_err("PSP: %s get vpsp_context failed from pid %d\n", __func__, cur_pid);
		return -ENOENT;
	}

	ctx->gpa_start = gpa_start;
	ctx->gpa_end = gpa_end;
	pr_info("PSP: set gpa range (start 0x%llx, end 0x%llx), by pid %d\n",
		gpa_start, gpa_end, cur_pid);
	return 0;
}

/**
 * Try to pin a page
 *
 * @vaddr: the userspace virtual address, must be aligned to PAGE_SIZE
 */
static int psp_pin_user_page(u64 vaddr)
{
	struct page *page;
	long npinned = 0;
	int ref_count = 0;

	// check must be aligned to PAGE_SIZE
	if (vaddr & (PAGE_SIZE - 1)) {
		pr_err("vaddr %llx not aligned to 0x%lx\n", vaddr, PAGE_SIZE);
		return -EFAULT;
	}

	npinned = pin_user_pages_fast(vaddr, 1, FOLL_WRITE, &page);
	if (npinned != 1) {
		pr_err("PSP: pin_user_pages_fast fail\n");
		return -ENOMEM;
	}

	ref_count = page_ref_count(page);
	pr_debug("pin user page with address %llx, page ref_count %d\n", vaddr, ref_count);
	return 0;
}

/**
 * Try to unpin a page
 *
 * @vaddr: the userspace virtual address, must be aligned to PAGE_SIZE
 */
static int psp_unpin_user_page(u64 vaddr)
{
	struct page *page;
	long npinned = 0;
	int ref_count = 0;

	// check must be aligned to PAGE_SIZE
	if (vaddr & (PAGE_SIZE - 1)) {
		pr_err("vaddr %llx not aligned to 0x%lx\n", vaddr, PAGE_SIZE);
		return -EFAULT;
	}

	// page reference count increment by 1
	npinned = get_user_pages_fast(vaddr, 1, FOLL_WRITE, &page);
	if (npinned != 1) {
		pr_err("PSP: pin_user_pages_fast fail\n");
		return -ENOMEM;
	}

	// page reference count decrement by 2
	put_page(page);
	put_page(page);

	ref_count = page_ref_count(page);
	pr_debug("unpin user page with address %llx, page ref_count %d\n", vaddr, ref_count);
	return 0;
}

static int do_vpsp_op_ioctl(struct vpsp_dev_ctrl *ctrl)
{
	int ret = 0;
@@ -299,6 +384,10 @@ static int do_vpsp_op_ioctl(struct vpsp_dev_ctrl *ctrl)
		ctrl->data.def_vid_perm = vpsp_get_default_vid_permission();
		break;

	case VPSP_OP_SET_GPA:
		ret = vpsp_set_gpa_range(ctrl->data.gpa.gpa_start, ctrl->data.gpa.gpa_end);
		break;

	default:
		ret = -EINVAL;
		break;
@@ -352,6 +441,14 @@ static long ioctl_psp(struct file *file, unsigned int ioctl, unsigned long arg)
			return -EFAULT;
		break;

	case HYGON_PSP_OP_PIN_USER_PAGE:
		ret = psp_pin_user_page((u64)arg);
		break;

	case HYGON_PSP_OP_UNPIN_USER_PAGE:
		ret = psp_unpin_user_page((u64)arg);
		break;

	default:
		pr_info("%s: invalid ioctl number: %d\n", __func__, opcode);
		return -EINVAL;
@@ -492,97 +589,6 @@ static int __psp_do_cmd_locked(int cmd, void *data, int *psp_ret)
	return ret;
}

int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret)
{
	struct psp_device *psp = psp_master;
	struct sev_device *sev;
	phys_addr_t phys_addr;
	unsigned int phys_lsb, phys_msb;
	unsigned int reg, ret = 0;

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

	if (*hygon_psp_hooks.psp_dead)
		return -EBUSY;

	sev = psp->sev_data;

	if (data && WARN_ON_ONCE(!virt_addr_valid(data)))
		return -EINVAL;

	/* Get the physical address of the command buffer */
	phys_addr = PUT_PSP_VID(__psp_pa(data), vid);
	phys_lsb = data ? lower_32_bits(phys_addr) : 0;
	phys_msb = data ? upper_32_bits(phys_addr) : 0;

	dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n",
		cmd, phys_msb, phys_lsb, *hygon_psp_hooks.psp_timeout);

	print_hex_dump_debug("(in):  ", DUMP_PREFIX_OFFSET, 16, 2, data,
			     hygon_psp_hooks.sev_cmd_buffer_len(cmd), false);

	iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg);
	iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg);

	sev->int_rcvd = 0;

	reg = FIELD_PREP(SEV_CMDRESP_CMD, cmd) | SEV_CMDRESP_IOC;
	iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg);

	/* wait for command completion */
	ret = hygon_psp_hooks.sev_wait_cmd_ioc(sev, &reg, *hygon_psp_hooks.psp_timeout);
	if (ret) {
		if (psp_ret)
			*psp_ret = 0;

		dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd);
		*hygon_psp_hooks.psp_dead = true;

		return ret;
	}

	*hygon_psp_hooks.psp_timeout = *hygon_psp_hooks.psp_cmd_timeout;

	if (psp_ret)
		*psp_ret = FIELD_GET(PSP_CMDRESP_STS, reg);

	if (FIELD_GET(PSP_CMDRESP_STS, reg)) {
		dev_dbg(sev->dev, "sev command %#x failed (%#010lx)\n",
			cmd, FIELD_GET(PSP_CMDRESP_STS, reg));
		ret = -EIO;
	}

	print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data,
			     hygon_psp_hooks.sev_cmd_buffer_len(cmd), false);

	return ret;
}

int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret)
{
	int rc;
	int mutex_enabled = READ_ONCE(hygon_psp_hooks.psp_mutex_enabled);

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

	rc = __vpsp_do_cmd_locked(vid, cmd, data, psp_ret);

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

	return rc;
}

int psp_do_cmd(int cmd, void *data, int *psp_ret)
{
	int rc;
+319 −435

File changed.

Preview size limit exceeded, changes collapsed.

Loading