Unverified Commit 305d3014 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!14820 [OLK-6.6]Hygon: Support kernel boot verification and secret injection for Hygon CSV3 VM

Merge Pull Request from: @hanliyang 
 
issue:
https://gitee.com/open_euler/dashboard?issue_id=IBGDLQ

- Kernel boot verification for Hygon CSV3 VM:
  The KVM must support issue LAUNCH_ENCRYPT_DATA with non-4k-aligned memory region, and
  must support issue LAUNCH_ENCRYPT_DATA more than once.

- Inject secret for Hygon CSV3 VM:
  KVM should not call sev_pin_memory() to pin the secret area of the CSV3 VM, and should
  provide GPA of the secret area to CSV3 firmware. 
 
Link:https://gitee.com/openeuler/kernel/pulls/14820

 

Reviewed-by: default avatarKevin Zhu <zhukeqian1@huawei.com>
Reviewed-by: default avatarWenkuan Wang <wenkuan.wang@amd.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 6d81e10f bf3598df
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -139,6 +139,8 @@ KVM_X86_OP_OPTIONAL(get_untagged_addr)
KVM_X86_OP_OPTIONAL(vm_attestation)
KVM_X86_OP_OPTIONAL(control_pre_system_reset)
KVM_X86_OP_OPTIONAL(control_post_system_reset)
KVM_X86_OP_OPTIONAL(get_hygon_coco_extension)
KVM_X86_OP_OPTIONAL(enable_hygon_coco_extension)

#undef KVM_X86_OP
#undef KVM_X86_OP_OPTIONAL
+2 −0
Original line number Diff line number Diff line
@@ -1778,6 +1778,8 @@ struct kvm_x86_ops {
	int (*vm_attestation)(struct kvm *kvm, unsigned long gpa, unsigned long len);
	int (*control_pre_system_reset)(struct kvm *kvm);
	int (*control_post_system_reset)(struct kvm *kvm);
	int (*get_hygon_coco_extension)(struct kvm *kvm);
	int (*enable_hygon_coco_extension)(struct kvm *kvm, u32 arg);
};

struct kvm_x86_nested_ops {
+457 −14
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/memory.h>
#include <linux/kvm_types.h>
#include <linux/rbtree.h>
#include <linux/swap.h>
#include <asm/cacheflush.h>
#include <asm/e820/api.h>
#include <asm/csv.h>
@@ -869,6 +870,13 @@ struct kvm_csv_info {

	struct list_head smr_list; /* List of guest secure memory regions */
	unsigned long nodemask; /* Nodemask where CSV3 guest's memory resides */

	/* The following 5 fields record the extension status for current VM */
	bool fw_ext_valid;	/* if @fw_ext field is valid */
	u32 fw_ext;		/* extensions supported by current platform */
	bool kvm_ext_valid;	/* if @kvm_ext field is valid */
	u32 kvm_ext;		/* extensions supported by KVM */
	u32 inuse_ext;		/* extensions inused by current VM */
};

struct kvm_svm_csv {
@@ -1035,7 +1043,7 @@ static bool csv3_is_mmio_pfn(kvm_pfn_t pfn)
				     E820_TYPE_RAM);
}

static int csv3_set_guest_private_memory(struct kvm *kvm)
static int csv3_set_guest_private_memory(struct kvm *kvm, struct kvm_sev_cmd *argp)
{
	struct kvm_memslots *slots = kvm_memslots(kvm);
	struct kvm_memory_slot *memslot;
@@ -1050,7 +1058,7 @@ static int csv3_set_guest_private_memory(struct kvm *kvm)
	LIST_HEAD(tmp_list);
	struct list_head *pos, *q;
	u32 i = 0, count = 0, remainder;
	int ret = 0, error;
	int ret = 0;
	u64 size = 0, nr_smr = 0, nr_pages = 0;
	u32 smr_entry_shift;
	int bkt;
@@ -1062,6 +1070,10 @@ static int csv3_set_guest_private_memory(struct kvm *kvm)
	if (!csv3_guest(kvm))
		return -ENOTTY;

	/* The smr_list should be initialized only once */
	if (!list_empty(&csv->smr_list))
		return -EFAULT;

	nodes_clear(nodemask);
	for_each_set_bit(i, &csv->nodemask, BITS_PER_LONG)
		if (i < MAX_NUMNODES)
@@ -1144,7 +1156,7 @@ static int csv3_set_guest_private_memory(struct kvm *kvm)
			/* set secury memory region for launch enrypt data */
			ret = hygon_kvm_hooks.sev_issue_cmd(kvm,
						CSV3_CMD_SET_GUEST_PRIVATE_MEMORY,
						set_guest_private_memory, &error);
						set_guest_private_memory, &argp->error);
			if (ret)
				goto e_free_smr;

@@ -1176,7 +1188,12 @@ static int csv3_set_guest_private_memory(struct kvm *kvm)
	return ret;
}

static int csv3_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
/**
 * csv3_launch_encrypt_data_alt_1 - The legacy handler to encrypt CSV3
 * guest's memory before VMRUN.
 */
static int csv3_launch_encrypt_data_alt_1(struct kvm *kvm,
					  struct kvm_sev_cmd *argp)
{
	struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info;
	struct kvm_csv3_launch_encrypt_data params;
@@ -1190,9 +1207,6 @@ static int csv3_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
	unsigned long pfn, pfn_sme_mask;
	int ret = 0;

	if (!csv3_guest(kvm))
		return -ENOTTY;

	if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data,
			   sizeof(params))) {
		ret = -EFAULT;
@@ -1204,10 +1218,17 @@ static int csv3_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
		goto exit;
	}

	/*
	 * If userspace request to invoke CSV3_CMD_SET_GUEST_PRIVATE_MEMORY
	 * explicitly, we should not calls to csv3_set_guest_private_memory()
	 * here.
	 */
	if (!(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM)) {
		/* Allocate all the guest memory from CMA */
	ret = csv3_set_guest_private_memory(kvm);
		ret = csv3_set_guest_private_memory(kvm, argp);
		if (ret)
			goto exit;
	}

	num_entries = params.len / PAGE_SIZE;
	num_entries_in_block = ARRAY_SIZE(blocks->entry);
@@ -1277,6 +1298,225 @@ static int csv3_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
	return ret;
}

#define MAX_ENTRIES_PER_BLOCK							\
	(sizeof(((struct encrypt_data_block *)0)->entry) /			\
	 sizeof(((struct encrypt_data_block *)0)->entry[0]))
#define MAX_BLOCKS_PER_CSV3_LUP_DATA						\
	(sizeof(((struct csv3_data_launch_encrypt_data *)0)->data_blocks) /	\
	 sizeof(((struct csv3_data_launch_encrypt_data *)0)->data_blocks[0]))
#define MAX_ENTRIES_PER_CSV3_LUP_DATA						\
	(MAX_BLOCKS_PER_CSV3_LUP_DATA * MAX_ENTRIES_PER_BLOCK)

/**
 * __csv3_launch_encrypt_data - The helper for handler
 * csv3_launch_encrypt_data_alt_2.
 */
static int __csv3_launch_encrypt_data(struct kvm *kvm,
				      struct kvm_sev_cmd *argp,
				      struct kvm_csv3_launch_encrypt_data *params,
				      void *src_buf,
				      unsigned int start_pgoff,
				      unsigned int end_pgoff)
{
	struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info;
	struct csv3_data_launch_encrypt_data *data = NULL;
	struct encrypt_data_block *block = NULL;
	struct page **pages = NULL;
	unsigned long len, remain_len;
	unsigned long pfn, pfn_sme_mask, last_pfn;
	unsigned int pgoff = start_pgoff;
	int i, j;
	int ret = -ENOMEM;

	/* Alloc command buffer for CSV3_CMD_LAUNCH_ENCRYPT_DATA command */
	data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT);
	if (!data)
		return -ENOMEM;

	/* Alloc pages for data_blocks[] in the command buffer */
	len = ARRAY_SIZE(data->data_blocks) * sizeof(struct page *);
	pages = kzalloc(len, GFP_KERNEL_ACCOUNT);
	if (!pages)
		goto e_free_data;

	for (i = 0; i < ARRAY_SIZE(data->data_blocks); i++) {
		pages[i] = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
		if (!pages[i])
			goto e_free_pages;
	}

	i = 0;
	while (i < ARRAY_SIZE(data->data_blocks) && pgoff < end_pgoff) {
		block = (struct encrypt_data_block *)page_to_virt(pages[i]);

		j = 0;
		last_pfn = 0;
		while (j < ARRAY_SIZE(block->entry) && pgoff < end_pgoff) {
			pfn = vmalloc_to_pfn(src_buf + (pgoff << PAGE_SHIFT));
			pfn_sme_mask = __sme_set(pfn << PAGE_SHIFT) >> PAGE_SHIFT;

			/*
			 * One entry can record a number of contiguous physical
			 * pages. If the current page is not adjacent to the
			 * previous physical page, we should record the page to
			 * the next entry. If entries of current block is used
			 * up, we should try the next block.
			 */
			if (last_pfn && (last_pfn + 1 == pfn)) {
				block->entry[j].npages++;
			} else if (j < (ARRAY_SIZE(block->entry) - 1)) {
				/* @last_pfn == 0 means fill in entry[0] */
				if (likely(last_pfn != 0))
					j++;
				block->entry[j].pfn = pfn_sme_mask;
				block->entry[j].npages = 1;
			} else {
				break;
			}

			/*
			 * Succeed to record one page, increase the page offset.
			 * We also record the pfn of current page so that we can
			 * record the contiguous physical pages into one entry.
			 */
			last_pfn = pfn;
			pgoff++;
		}

		i++;
	}

	if (pgoff < end_pgoff) {
		pr_err("CSV3: Fail to fill in LAUNCH_ENCRYPT_DATA command!\n");
		goto e_free_pages;
	}

	len = (end_pgoff - start_pgoff) << PAGE_SHIFT;
	clflush_cache_range(src_buf + (start_pgoff << PAGE_SHIFT), len);

	/* Fill in command buffer */
	data->handle = csv->sev->handle;

	if (start_pgoff == 0) {
		data->gpa = params->gpa;
		len -= params->gpa & ~PAGE_MASK;
	} else {
		data->gpa = (params->gpa & PAGE_MASK) + (start_pgoff << PAGE_SHIFT);
	}
	remain_len = params->len - (data->gpa - params->gpa);

	data->length = (len <= remain_len) ? len : remain_len;

	for (j = 0; j < i; j++)
		data->data_blocks[j] = __sme_set(page_to_phys(pages[j]));

	/* Issue command */
	ret = hygon_kvm_hooks.sev_issue_cmd(kvm, CSV3_CMD_LAUNCH_ENCRYPT_DATA,
							data, &argp->error);

e_free_pages:
	for (i = 0; i < ARRAY_SIZE(data->data_blocks); i++) {
		if (pages[i])
			__free_page(pages[i]);
	}
	kfree(pages);
e_free_data:
	kfree(data);

	return ret;
}

/**
 * csv3_launch_encrypt_data_alt_2 - The handler to support encrypt CSV3
 * guest's memory before VMRUN. This handler support issue API command
 * multiple times, both the GPA and length of the memory region are not
 * required to be 4K-aligned.
 */
static int csv3_launch_encrypt_data_alt_2(struct kvm *kvm,
					  struct kvm_sev_cmd *argp)
{
	struct kvm_csv3_launch_encrypt_data params;
	void *buffer = NULL;
	unsigned long len;
	unsigned int total_pages, start_pgoff, next_pgoff;
	int ret = 0;

	if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data,
			   sizeof(params))) {
		return -EFAULT;
	}

	/* Both the GPA and length must be 16 Bytes aligned at least */
	if (!params.len ||
	    !params.uaddr ||
	    !IS_ALIGNED(params.len, 16) ||
	    !IS_ALIGNED(params.gpa, 16)) {
		return -EINVAL;
	}

	/*
	 * Alloc buffer to save source data. When we copy source data from
	 * userspace to the buffer, the data in the first page of the buffer
	 * should keep the offset as params.gpa.
	 */
	len = PAGE_ALIGN((params.gpa & ~PAGE_MASK) + params.len);
	total_pages = len >> PAGE_SHIFT;
	next_pgoff = 0;

	buffer = vzalloc(len);
	if (!buffer)
		return -ENOMEM;

	if (copy_from_user(buffer + (params.gpa & ~PAGE_MASK),
			   (void __user *)params.uaddr, params.len)) {
		ret = -EFAULT;
		goto e_free_buffer;
	}

	/*
	 * If the source data is too large, we should issue command more than
	 * once. The LAUNCH_ENCRYPT_DATA API updates not only the measurement
	 * of the data, but also the measurement of the metadata correspond to
	 * the data. The guest owner is obligated to verify the launch
	 * measurement, so guest owner must be aware of the launch measurement
	 * of each LAUNCH_ENCRYPT_DATA API command. If we processing pages more
	 * than MAX_ENTRIES_PER_CSV3_LUP_DATA in each API command, the guest
	 * owner could not able to calculate the correct measurement and fail
	 * to verify the launch measurement. For this reason, we limit the
	 * maximum number of pages processed by each API command to
	 * MAX_ENTRIES_PER_CSV3_LUP_DATA.
	 */
	while (next_pgoff < total_pages) {
		start_pgoff = next_pgoff;
		next_pgoff += MAX_ENTRIES_PER_CSV3_LUP_DATA;

		if (next_pgoff > total_pages)
			next_pgoff = total_pages;

		ret = __csv3_launch_encrypt_data(kvm, argp, &params,
						 buffer, start_pgoff, next_pgoff);
		if (ret)
			goto e_free_buffer;
	}

e_free_buffer:
	vfree(buffer);
	return ret;
}

static int csv3_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
{
	struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info;

	if (!csv3_guest(kvm))
		return -ENOTTY;

	if (!(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA))
		return csv3_launch_encrypt_data_alt_1(kvm, argp);

	return csv3_launch_encrypt_data_alt_2(kvm, argp);
}

static int csv3_sync_vmsa(struct vcpu_svm *svm)
{
	struct sev_es_save_area *save = svm->sev_es.vmsa;
@@ -1285,14 +1525,43 @@ static int csv3_sync_vmsa(struct vcpu_svm *svm)
	if (svm->vcpu.guest_debug || (svm->vmcb->save.dr7 & ~DR7_FIXED_1))
		return -EINVAL;

	/*
	 * CSV3 will use a VMSA that is pointed to by the VMCB, not
	 * the traditional VMSA that is part of the VMCB. Copy the
	 * traditional VMSA as it has been built so far (in prep
	 * for LAUNCH_ENCRYPT_VMCB) to be the initial CSV3 state.
	 */
	memcpy(save, &svm->vmcb->save, sizeof(svm->vmcb->save));

	/* Sync registgers per spec. */
	save->rax = svm->vcpu.arch.regs[VCPU_REGS_RAX];
	save->rbx = svm->vcpu.arch.regs[VCPU_REGS_RBX];
	save->rcx = svm->vcpu.arch.regs[VCPU_REGS_RCX];
	save->rdx = svm->vcpu.arch.regs[VCPU_REGS_RDX];
	save->rsp = svm->vcpu.arch.regs[VCPU_REGS_RSP];
	save->rbp = svm->vcpu.arch.regs[VCPU_REGS_RBP];
	save->rsi = svm->vcpu.arch.regs[VCPU_REGS_RSI];
	save->rdi = svm->vcpu.arch.regs[VCPU_REGS_RDI];
#ifdef CONFIG_X86_64
	save->r8  = svm->vcpu.arch.regs[VCPU_REGS_R8];
	save->r9  = svm->vcpu.arch.regs[VCPU_REGS_R9];
	save->r10 = svm->vcpu.arch.regs[VCPU_REGS_R10];
	save->r11 = svm->vcpu.arch.regs[VCPU_REGS_R11];
	save->r12 = svm->vcpu.arch.regs[VCPU_REGS_R12];
	save->r13 = svm->vcpu.arch.regs[VCPU_REGS_R13];
	save->r14 = svm->vcpu.arch.regs[VCPU_REGS_R14];
	save->r15 = svm->vcpu.arch.regs[VCPU_REGS_R15];
#endif
	save->rip = svm->vcpu.arch.regs[VCPU_REGS_RIP];

	/* Sync some non-GPR registers before encrypting */
	save->xcr0 = svm->vcpu.arch.xcr0;
	save->pkru = svm->vcpu.arch.pkru;
	save->xss  = svm->vcpu.arch.ia32_xss;
	save->dr6  = svm->vcpu.arch.dr6;

	pr_debug("Virtual Machine Save Area (VMSA):\n");
	print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, save, sizeof(*save), false);

	return 0;
}
@@ -1639,7 +1908,7 @@ static int csv3_receive_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp)

	if (unlikely(list_empty(&csv->smr_list))) {
		/* Allocate all the guest memory from CMA */
		ret = csv3_set_guest_private_memory(kvm);
		ret = csv3_set_guest_private_memory(kvm, argp);
		if (ret)
			goto exit;
	}
@@ -2330,6 +2599,95 @@ static int csv3_handle_memory(struct kvm *kvm, struct kvm_sev_cmd *argp)
	return r;
};

static int csv_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp)
{
	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
	struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info;
	struct sev_data_launch_secret data;
	struct kvm_sev_launch_secret params;
	struct page **pages;
	void *blob, *hdr;
	unsigned long n, i;
	int ret, offset;

	if (!sev_guest(kvm))
		return -ENOTTY;

	if (copy_from_user(&params, (void __user *)(uintptr_t)argp->data, sizeof(params)))
		return -EFAULT;

	memset(&data, 0, sizeof(data));

	if (!csv3_guest(kvm) ||
	    !(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET)) {
		pages = hygon_kvm_hooks.sev_pin_memory(kvm, params.guest_uaddr,
						       params.guest_len, &n, 1);
		if (IS_ERR(pages))
			return PTR_ERR(pages);

		/*
		 * Flush (on non-coherent CPUs) before LAUNCH_SECRET encrypts
		 * pages in place; the cache may contain the data that was
		 * written unencrypted.
		 */
		hygon_kvm_hooks.sev_clflush_pages(pages, n);

		/*
		 * The secret must be copied into contiguous memory region,
		 * lets verify that userspace memory pages are contiguous
		 * before we issue command.
		 */
		if (hygon_kvm_hooks.get_num_contig_pages(0, pages, n) != n) {
			ret = -EINVAL;
			goto e_unpin_memory;
		}

		offset = params.guest_uaddr & (PAGE_SIZE - 1);
		data.guest_address = __sme_page_pa(pages[0]) + offset;
	} else {
		/* It's gpa for CSV3 guest */
		data.guest_address = params.guest_uaddr;
	}
	data.guest_len = params.guest_len;

	blob = psp_copy_user_blob(params.trans_uaddr, params.trans_len);
	if (IS_ERR(blob)) {
		ret = PTR_ERR(blob);
		goto e_unpin_memory;
	}

	data.trans_address = __psp_pa(blob);
	data.trans_len = params.trans_len;

	hdr = psp_copy_user_blob(params.hdr_uaddr, params.hdr_len);
	if (IS_ERR(hdr)) {
		ret = PTR_ERR(hdr);
		goto e_free_blob;
	}
	data.hdr_address = __psp_pa(hdr);
	data.hdr_len = params.hdr_len;

	data.handle = sev->handle;
	ret = hygon_kvm_hooks.sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_SECRET,
							&data, &argp->error);

	kfree(hdr);

e_free_blob:
	kfree(blob);
e_unpin_memory:
	if (!csv3_guest(kvm) ||
	    !(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET)) {
		/* content of memory is updated, mark pages dirty */
		for (i = 0; i < n; i++) {
			set_page_dirty_lock(pages[i]);
			mark_page_accessed(pages[i]);
		}
		hygon_kvm_hooks.sev_unpin_memory(kvm, pages, n);
	}
	return ret;
}

static int csv_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
{
	struct kvm_sev_cmd sev_cmd;
@@ -2353,6 +2711,9 @@ static int csv_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
		r = csv_command_batch(kvm, &sev_cmd);
		mutex_unlock(&csv_cmd_batch_mutex);
		break;
	case KVM_SEV_LAUNCH_SECRET:
		r = csv_launch_secret(kvm, &sev_cmd);
		break;
	case KVM_SEV_SEND_UPDATE_VMSA:
		/*
		 * Hygon implement the specific interface, although
@@ -2399,6 +2760,9 @@ static int csv_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
	case KVM_CSV3_HANDLE_MEMORY:
		r = csv3_handle_memory(kvm, &sev_cmd);
		break;
	case KVM_CSV3_SET_GUEST_PRIVATE_MEMORY:
		r = csv3_set_guest_private_memory(kvm, &sev_cmd);
		break;
	default:
		/*
		 * If the command is compatible between CSV and SEV, the
@@ -2661,6 +3025,84 @@ static void csv_free_asid_userid_array(void)

#endif	/* CONFIG_KVM_SUPPORTS_CSV_REUSE_ASID */

/**
 * When userspace recognizes these extensions, it is suggested that the userspace
 * enables these extensions through KVM_ENABLE_CAP, so that both the userspace
 * and KVM can utilize these extensions.
 */
static int csv_get_hygon_coco_extension(struct kvm *kvm)
{
	struct kvm_csv_info *csv;
	size_t len = sizeof(uint32_t);
	int ret = 0;

	if (!kvm)
		return 0;

	csv = &to_kvm_svm_csv(kvm)->csv_info;

	if (csv->fw_ext_valid == false) {
		ret = csv_get_extension_info(&csv->fw_ext, &len);

		if (ret == -ENODEV) {
			pr_err("Unable to interact with CSV firmware!\n");
			return 0;
		} else if (ret == -EINVAL) {
			pr_err("Need %ld bytes to record fw extension!\n", len);
			return 0;
		}

		csv->fw_ext_valid = true;
	}

	/* The kvm_ext field of kvm_csv_info is filled in only if the fw_ext
	 * field of kvm_csv_info is valid.
	 */
	if (csv->kvm_ext_valid == false) {
		if (csv3_guest(kvm)) {
			csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM;
			if (csv->fw_ext & CSV_EXT_CSV3_MULT_LUP_DATA)
				csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA;
			if (csv->fw_ext & CSV_EXT_CSV3_INJ_SECRET)
				csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET;
		}
		csv->kvm_ext_valid = true;
	}

	/* Return extension info only if both fw_ext and kvm_ext fields of
	 * kvm_csv_info are valid.
	 */
	pr_debug("%s: fw_ext=%#x kvm_ext=%#x\n",
		 __func__, csv->fw_ext, csv->kvm_ext);
	return (int)csv->kvm_ext;
}

/**
 * Return 0 means KVM accept the negotiation from userspace. Both the
 * userspace and KVM should not utilise extensions if failed to negotiate.
 */
static int csv_enable_hygon_coco_extension(struct kvm *kvm, u32 arg)
{
	struct kvm_csv_info *csv;

	if (!kvm)
		return -EINVAL;

	csv = &to_kvm_svm_csv(kvm)->csv_info;

	/* Negotiation is accepted only if both the fw_ext and kvm_ext fields
	 * of kvm_csv_info are valid and the virtual machine is a CSV3 guest.
	 */
	if (csv->fw_ext_valid && csv->kvm_ext_valid && csv3_guest(kvm)) {
		csv->inuse_ext = csv->kvm_ext & arg;
		pr_debug("%s: inuse_ext=%#x\n", __func__, csv->inuse_ext);
		return csv->inuse_ext;
	}

	/* Userspace should not utilise the extensions */
	return -EINVAL;
}

void __init csv_hardware_setup(unsigned int max_csv_asid)
{
	unsigned int nr_asids = max_csv_asid + 1;
@@ -2709,14 +3151,15 @@ void __init csv_init(struct kvm_x86_ops *ops)

	memcpy(&csv_x86_ops, ops, sizeof(struct kvm_x86_ops));

	ops->vm_size = sizeof(struct kvm_svm_csv);
	ops->mem_enc_ioctl = csv_mem_enc_ioctl;
	ops->vm_attestation = csv_vm_attestation;
	ops->control_pre_system_reset = csv_control_pre_system_reset;
	ops->control_post_system_reset = csv_control_post_system_reset;
	ops->get_hygon_coco_extension = csv_get_hygon_coco_extension;
	ops->enable_hygon_coco_extension = csv_enable_hygon_coco_extension;

	if (boot_cpu_has(X86_FEATURE_SEV_ES) && boot_cpu_has(X86_FEATURE_CSV3)) {
		ops->vm_size = sizeof(struct kvm_svm_csv);

		ops->vm_destroy = csv_vm_destroy;
		ops->handle_exit = csv_handle_exit;
		ops->guest_memory_reclaimed = csv_guest_memory_reclaimed;
+23 −0
Original line number Diff line number Diff line
@@ -4722,6 +4722,18 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
			r = static_call(kvm_x86_has_emulated_msr)(kvm,
							MSR_AMD64_SEV_ES_GHCB);
		break;
	case KVM_CAP_HYGON_COCO_EXT:
		r = 0;

		/*
		 * Before running a Hygon confidential guest, the userspace
		 * should find the advanced extensions of the Hygon CSV
		 * technology. If the userspace recognize the extensions, it's
		 * suggested that the userspace to utilise extensions.
		 */
		if (is_x86_vendor_hygon() && kvm_x86_ops.get_hygon_coco_extension)
			r = static_call(kvm_x86_get_hygon_coco_extension)(kvm);
		break;
	default:
		break;
	}
@@ -6599,6 +6611,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
		}
		mutex_unlock(&kvm->lock);
		break;
	case KVM_CAP_HYGON_COCO_EXT:
		r = -EINVAL;

		/*
		 * The userspace negotiate with KVM to utilise extensions of
		 * Hygon CSV technology.
		 */
		if (is_x86_vendor_hygon() && kvm_x86_ops.enable_hygon_coco_extension)
			r = static_call(kvm_x86_enable_hygon_coco_extension)(kvm,
								(u32)cap->args[0]);
		break;
	default:
		r = -EINVAL;
		break;
+83 −2
Original line number Diff line number Diff line
@@ -128,6 +128,45 @@ static int csv_ioctl_do_hgsc_import(struct sev_issue_cmd *argp)
	return ret;
}

static int csv_get_api_version(void)
{
	struct sev_device *sev;
	struct sev_user_data_status *status;
	int error = 0, ret;

	if (!hygon_psp_hooks.sev_dev_hooks_installed)
		return -ENODEV;

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

	sev = psp_master->sev_data;

	status = kzalloc(sizeof(*status), GFP_KERNEL);
	if (!status)
		return -ENOMEM;

	ret = hygon_psp_hooks.__sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS,
						  status, &error);
	if (ret) {
		dev_err(sev->dev,
			"CSV: failed to get status. Error: %#x\n", error);
		goto e_free_status;
	}

	sev->api_major = status->api_major;
	sev->api_minor = status->api_minor;
	sev->build = status->build;
	sev->state = status->state;

	csv_update_api_version(status);

	ret = 0;
e_free_status:
	kfree(status);
	return ret;
}

static int csv_ioctl_do_download_firmware(struct sev_issue_cmd *argp)
{
	struct sev_data_download_firmware *data = NULL;
@@ -185,10 +224,20 @@ static int csv_ioctl_do_download_firmware(struct sev_issue_cmd *argp)

	ret = hygon_psp_hooks.__sev_do_cmd_locked(SEV_CMD_DOWNLOAD_FIRMWARE,
						  data, &argp->error);
	if (ret)
	if (ret) {
		pr_err("Failed to update CSV firmware: %#x\n", argp->error);
	else
		goto err_free_page;
	} else {
		pr_info("CSV firmware update successful\n");
	}

	/*
	 * Synchronize API version status. The return value of csv_get_api_version
	 * will inform the user of any error encountered when attempting to
	 * communicate with the Hygon PSP after the DOWNLOAD_FIRMWARE API completes
	 * successfully.
	 */
	ret = csv_get_api_version();

err_free_page:
	__free_pages(p, order);
@@ -615,6 +664,38 @@ int csv_check_stat_queue_status(int *psp_ret)
}
EXPORT_SYMBOL_GPL(csv_check_stat_queue_status);

int csv_get_extension_info(void *buf, size_t *size)
{
	/* If @hygon_csv_build is 0, this means CSV firmware doesn't exist or
	 * the psp device doesn't exist.
	 */
	if (hygon_csv_build == 0)
		return -ENODEV;

	/* The caller must provide valid @buf and the @buf must >= 4 bytes in
	 * size.
	 */
	if (!buf || !size || *size < sizeof(uint32_t)) {
		if (size)
			*size = sizeof(uint32_t);

		return -EINVAL;
	}

	/* Since firmware with build id 2200, support:
	 *   a. issue LAUNCH_ENCRYPT_DATA command more than once for a
	 *      CSV3 guest.
	 *   b. inject secret to a CSV3 guest.
	 */
	if (csv_version_greater_or_equal(2200)) {
		*(uint32_t *)buf |= CSV_EXT_CSV3_MULT_LUP_DATA;
		*(uint32_t *)buf |= CSV_EXT_CSV3_INJ_SECRET;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(csv_get_extension_info);

#ifdef CONFIG_HYGON_CSV

int csv_platform_cmd_set_secure_memory_region(struct sev_device *sev, int *error)
Loading