Unverified Commit 793da324 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!9955 [OLK-6.6]HYGON: CSV3 patch series part 1 (Secure memory management and...

!9955 [OLK-6.6]HYGON: CSV3 patch series part 1 (Secure memory management and initialization for CSV3)

Merge Pull Request from: @hanliyang 
 
CSV3 patch series part 1 (Secure memory management and initialization for CSV3)

issue:
https://gitee.com/open_euler/dashboard?issue_id=IAD13S

CSV3 provides an enhancement technology named memory isolation to improve the security. A
dedicated memory isolation hardware is built in Hygon hardware. Only the secure processor
has privilege to configure the isolation hardware. The VMM allocates CMA memory and transfers
them to secure processor. The secure processor maps the memory to secure nested page table
and manages them as guest's private memory. Any memory access (read or write) to CSV3 guest's
private memory outside the guest will be blocked by isolation hardware.

Hygon CSV3 make use of the Linux kernel's CMA feature to support contiguous memory allocations
and deallocations.

This patch series introduce command identifier for CSV3, and provide code to support secure memory initialization and management for CSV3. 
 
Link:https://gitee.com/openeuler/kernel/pulls/9955

 

Reviewed-by: default avatarKevin Zhu <zhukeqian1@huawei.com>
Reviewed-by: default avatarZhang Peng <zhangpeng362@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 7b9de8aa a7251227
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Hygon China Secure Virtualization (CSV)
 *
 * Copyright (C) Hygon Info Technologies Ltd.
 *
 * Author: Jiang Xin <jiangxin@hygon.cn>
 */

#ifndef __ASM_X86_CSV_H__
#define __ASM_X86_CSV_H__

#ifndef __ASSEMBLY__

#ifdef CONFIG_HYGON_CSV

struct csv_mem {
	uint64_t start;
	uint64_t size;
};

#define CSV_MR_ALIGN_BITS		(28)

extern struct csv_mem *csv_smr;
extern unsigned int csv_smr_num;

void __init early_csv_reserve_mem(void);

phys_addr_t csv_alloc_from_contiguous(size_t size, nodemask_t *nodes_allowed,
				      unsigned int align);
void csv_release_to_contiguous(phys_addr_t pa, size_t size);

uint32_t csv_get_smr_entry_shift(void);

#else	/* !CONFIG_HYGON_CSV */

#define csv_smr		NULL
#define csv_smr_num	0U

static inline void __init early_csv_reserve_mem(void) { }

static inline phys_addr_t
csv_alloc_from_contiguous(size_t size, nodemask_t *nodes_allowed,
			  unsigned int align) { return 0; }
static inline void csv_release_to_contiguous(phys_addr_t pa, size_t size) { }

static inline uint32_t csv_get_smr_entry_shift(void) { return 0; }

#endif	/* CONFIG_HYGON_CSV */

#endif	/* __ASSEMBLY__ */

#endif	/* __ASM_X86_CSV_H__ */
+11 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@
#include <asm/unwind.h>
#include <asm/vsyscall.h>
#include <linux/vmalloc.h>
#include <asm/csv.h>

/*
 * max_low_pfn_mapped: highest directly mapped pfn < 4 GB
@@ -1217,6 +1218,16 @@ void __init setup_arch(char **cmdline_p)
	early_acpi_boot_init();

	initmem_init();

	/*
	 * Try to reserve contiguous memory to support CSV3. The
	 * contiguous memory will be reserved iff running on bare metal
	 * and support CSV3 feature.
	 * Note: If CONFIG_HYGON_CSV is not set, early_csv_reserve_mem
	 * will do nothing.
	 */
	early_csv_reserve_mem();

	dma_contiguous_reserve(max_pfn_mapped << PAGE_SHIFT);

	if (boot_cpu_has(X86_FEATURE_GBPAGES))
+4 −1
Original line number Diff line number Diff line
@@ -516,6 +516,7 @@ static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr,
	unsigned long locked, lock_limit;
	struct page **pages;
	unsigned long first, last;
	unsigned int flags = 0;
	int ret;

	lockdep_assert_held(&kvm->lock);
@@ -548,8 +549,10 @@ static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr,
	if (!pages)
		return ERR_PTR(-ENOMEM);

	flags = write ? FOLL_WRITE : 0;

	/* Pin the user virtual address. */
	npinned = pin_user_pages_fast(uaddr, npages, write ? FOLL_WRITE : 0, pages);
	npinned = pin_user_pages_fast(uaddr, npages, flags | FOLL_LONGTERM, pages);
	if (npinned != npages) {
		pr_err("SEV: Failure locking %lu pages.\n", npages);
		ret = -ENOMEM;
+404 −0
Original line number Diff line number Diff line
@@ -16,6 +16,15 @@
#include <linux/cc_platform.h>
#include <linux/mem_encrypt.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/memblock.h>
#include <linux/cma.h>
#include <linux/minmax.h>
#include <asm/io.h>
#include <asm/cacheflush.h>
#include <asm/set_memory.h>
#include <asm/csv.h>
#include <asm/processor-hygon.h>

void print_hygon_cc_feature_info(void)
{
@@ -37,3 +46,398 @@ void print_hygon_cc_feature_info(void)
	if (cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
		pr_info(" HYGON CSV2");
}

/*
 * Check whether host supports CSV3 in hygon platform.
 * Called in the guest, it always returns false.
 */
static bool __init __maybe_unused csv3_check_cpu_support(void)
{
	unsigned int eax, ebx, ecx, edx;
	unsigned long me_mask;
	u64 msr;
	bool csv3_enabled;

	if (!is_x86_vendor_hygon())
		return false;

	if (sev_status)
		return false;

	/* Check for the SME/CSV support leaf */
	eax = 0x80000000;
	ecx = 0;
	native_cpuid(&eax, &ebx, &ecx, &edx);
	if (eax < 0x8000001f)
		return false;

#define HYGON_SME_BIT	BIT(0)
#define HYGON_CSV3_BIT	BIT(30)
	/*
	 * Check for the CSV feature:
	 * CPUID Fn8000_001F[EAX]
	 * - Bit 0  - SME support
	 * - Bit 1  - CSV support
	 * - Bit 3  - CSV2 support
	 * - Bit 30 - CSV3 support
	 */
	eax = 0x8000001f;
	ecx = 0;
	native_cpuid(&eax, &ebx, &ecx, &edx);
	if (!(eax & HYGON_SME_BIT))
		return false;

	csv3_enabled = !!(eax & HYGON_CSV3_BIT);

	me_mask = 1UL << (ebx & 0x3f);

	/* No SME if Hypervisor bit is set */
	eax = 1;
	ecx = 0;
	native_cpuid(&eax, &ebx, &ecx, &edx);
	if (ecx & BIT(31))
		return false;

	/* For SME, check the SYSCFG MSR */
	msr = __rdmsr(MSR_AMD64_SYSCFG);
	if (!(msr & MSR_AMD64_SYSCFG_MEM_ENCRYPT))
		return false;

	return !!me_mask && csv3_enabled;
}

/******************************************************************************/
/**************************** CSV3 CMA interfaces *****************************/
/******************************************************************************/

/* 0 percent of total memory by default*/
static unsigned char csv_mem_percentage;
static unsigned long csv_mem_size;

static int __init cmdline_parse_csv_mem_size(char *str)
{
	unsigned long size;
	char *endp;

	if (str) {
		size  = memparse(str, &endp);
		csv_mem_size = size;
		if (!csv_mem_size)
			csv_mem_percentage = 0;
	}

	return 0;
}
early_param("csv_mem_size", cmdline_parse_csv_mem_size);

static int __init cmdline_parse_csv_mem_percentage(char *str)
{
	unsigned char percentage;
	int ret;

	if (!str)
		return 0;

	ret  = kstrtou8(str, 10, &percentage);
	if (!ret) {
		csv_mem_percentage = min_t(unsigned char, percentage, 80);
		if (csv_mem_percentage != percentage)
			pr_warn("csv_mem_percentage is limited to 80.\n");
	} else {
		/* Disable CSV CMA. */
		csv_mem_percentage = 0;
		pr_err("csv_mem_percentage is invalid. (0 - 80) is expected.\n");
	}

	return ret;
}
early_param("csv_mem_percentage", cmdline_parse_csv_mem_percentage);

#define NUM_SMR_ENTRIES			(8 * 1024)
#define CSV_CMA_SHIFT			PUD_SHIFT
#define CSV_CMA_SIZE			(1 << CSV_CMA_SHIFT)
#define MIN_SMR_ENTRY_SHIFT		23
#define CSV_SMR_INFO_SIZE		(nr_node_ids * sizeof(struct csv_mem))

struct csv_mem *csv_smr;
EXPORT_SYMBOL_GPL(csv_smr);

unsigned int csv_smr_num;
EXPORT_SYMBOL_GPL(csv_smr_num);

#ifdef CONFIG_CMA

struct csv_cma {
	int fast;
	struct cma *cma;
};

struct cma_array {
	unsigned long count;
	struct csv_cma csv_cma[];
};

static unsigned int smr_entry_shift;
static struct cma_array *csv_contiguous_pernuma_area[MAX_NUMNODES];

static void csv_set_smr_entry_shift(unsigned int shift)
{
	smr_entry_shift = max_t(unsigned int, shift, MIN_SMR_ENTRY_SHIFT);
	pr_info("CSV-CMA: SMR entry size is 0x%x\n", 1 << smr_entry_shift);
}

unsigned int csv_get_smr_entry_shift(void)
{
	return smr_entry_shift;
}
EXPORT_SYMBOL_GPL(csv_get_smr_entry_shift);

static unsigned long __init present_pages_in_node(int nid)
{
	unsigned long range_start_pfn, range_end_pfn;
	unsigned long nr_present = 0;
	int i;

	for_each_mem_pfn_range(i, nid, &range_start_pfn, &range_end_pfn, NULL)
		nr_present += range_end_pfn - range_start_pfn;

	return nr_present;
}

static phys_addr_t __init csv_early_percent_memory_on_node(int nid)
{
	return (present_pages_in_node(nid) * csv_mem_percentage / 100) << PAGE_SHIFT;
}

static void __init csv_cma_reserve_mem(void)
{
	int node, i;
	unsigned long size;
	int idx = 0;
	int count;
	int cma_array_size;
	unsigned long max_spanned_size = 0;

	csv_smr = memblock_alloc_node(CSV_SMR_INFO_SIZE, SMP_CACHE_BYTES, NUMA_NO_NODE);
	if (!csv_smr) {
		pr_err("CSV-CMA: Fail to allocate csv_smr\n");
		return;
	}

	for_each_node_state(node, N_ONLINE) {
		int ret;
		char name[CMA_MAX_NAME];
		struct cma_array *array;
		unsigned long spanned_size;
		unsigned long start = 0, end = 0;
		struct csv_cma *csv_cma;

		size = csv_early_percent_memory_on_node(node);
		count = DIV_ROUND_UP(size, 1 << CSV_CMA_SHIFT);
		if (!count)
			continue;

		cma_array_size = count * sizeof(*csv_cma) + sizeof(*array);
		array = memblock_alloc_node(cma_array_size, SMP_CACHE_BYTES, NUMA_NO_NODE);
		if (!array) {
			pr_err("CSV-CMA: Fail to allocate cma_array\n");
			continue;
		}

		array->count = 0;
		csv_contiguous_pernuma_area[node] = array;

		for (i = 0; i < count; i++) {
			csv_cma = &array->csv_cma[i];
			csv_cma->fast = 1;
			snprintf(name, sizeof(name), "csv-n%dc%d", node, i);
			ret = cma_declare_contiguous_nid(0, CSV_CMA_SIZE, 0,
					1 << CSV_MR_ALIGN_BITS, PMD_SHIFT - PAGE_SHIFT,
					false, name, &(csv_cma->cma), node);
			if (ret) {
				pr_warn("CSV-CMA: Fail to reserve memory size 0x%x node %d\n",
					1 << CSV_CMA_SHIFT, node);
				break;
			}

			if (start > cma_get_base(csv_cma->cma) || !start)
				start = cma_get_base(csv_cma->cma);

			if (end < cma_get_base(csv_cma->cma) + cma_get_size(csv_cma->cma))
				end = cma_get_base(csv_cma->cma) + cma_get_size(csv_cma->cma);
		}

		if (!i)
			continue;

		array->count = i;
		spanned_size = end - start;
		if (spanned_size > max_spanned_size)
			max_spanned_size = spanned_size;

		csv_smr[idx].start = start;
		csv_smr[idx].size  = end - start;
		idx++;

		pr_info("CSV-CMA: Node %d - reserve size 0x%016lx, (expected size 0x%016lx)\n",
			node, (unsigned long)i * CSV_CMA_SIZE, size);
	}

	csv_smr_num = idx;
	WARN_ON((max_spanned_size / NUM_SMR_ENTRIES) < 1);
	if (likely((max_spanned_size / NUM_SMR_ENTRIES) >= 1))
		csv_set_smr_entry_shift(ilog2(max_spanned_size / NUM_SMR_ENTRIES - 1) + 1);
}

#define CSV_CMA_AREAS		2458

void __init early_csv_reserve_mem(void)
{
	unsigned long total_pages;

	/* Only reserve memory on the host that enabled CSV3 feature */
	if (!csv3_check_cpu_support())
		return;

	if (cma_alloc_areas(CSV_CMA_AREAS))
		return;

	total_pages = PHYS_PFN(memblock_phys_mem_size());
	if (csv_mem_size) {
		if (csv_mem_size < (total_pages << PAGE_SHIFT)) {
			csv_mem_percentage = csv_mem_size * 100 / (total_pages << PAGE_SHIFT);
			if (csv_mem_percentage > 80)
				csv_mem_percentage = 80; /* Maximum percentage */
		} else
			csv_mem_percentage = 80; /* Maximum percentage */
	}

	if (!csv_mem_percentage) {
		pr_warn("CSV-CMA: Don't reserve any memory\n");
		return;
	}

	csv_cma_reserve_mem();
}

phys_addr_t csv_alloc_from_contiguous(size_t size, nodemask_t *nodes_allowed,
				      unsigned int align)
{
	int nid;
	int nr_nodes;
	struct page *page = NULL;
	phys_addr_t phys_addr;
	int count;
	struct csv_cma *csv_cma;
	int fast = 1;

	if (!nodes_allowed || size > CSV_CMA_SIZE) {
		pr_err("CSV-CMA: Invalid params, size = 0x%lx, nodes_allowed = %p\n",
			size, nodes_allowed);
		return 0;
	}

	align = min_t(unsigned int, align, get_order(CSV_CMA_SIZE));
retry:
	nr_nodes = nodes_weight(*nodes_allowed);

	/* Traverse from current node */
	nid = numa_node_id();
	if (!node_isset(nid, *nodes_allowed))
		nid = next_node_in(nid, *nodes_allowed);

	for (; nr_nodes > 0; nid = next_node_in(nid, *nodes_allowed), nr_nodes--) {
		struct cma_array *array = csv_contiguous_pernuma_area[nid];

		if (!array)
			continue;

		count = array->count;
		while (count) {
			csv_cma = &array->csv_cma[count - 1];

			/*
			 * The value check of csv_cma->fast is lockless, but
			 * that's ok as this don't affect functional correntness
			 * whatever the value of csv_cma->fast.
			 */
			if (fast && !csv_cma->fast) {
				count--;
				continue;
			}
			page = cma_alloc(csv_cma->cma, PAGE_ALIGN(size) >> PAGE_SHIFT,
							align, true);
			if (page) {
				page->private = (unsigned long)csv_cma;
				if (!csv_cma->fast)
					csv_cma->fast = 1;
				goto success;
			} else
				csv_cma->fast = 0;

			count--;
		}
	}

	if (fast) {
		fast = 0;
		goto retry;
	} else {
		pr_err("CSV-CMA: Fail to alloc secure memory(size = 0x%lx)\n", size);
		return 0;
	}

success:
	phys_addr = page_to_phys(page);
	clflush_cache_range(__va(phys_addr), size);

	return phys_addr;
}
EXPORT_SYMBOL_GPL(csv_alloc_from_contiguous);

void csv_release_to_contiguous(phys_addr_t pa, size_t size)
{
	struct csv_cma *csv_cma;
	struct page *page = pfn_to_page(pa >> PAGE_SHIFT);

	WARN_ON(!page);
	if (likely(page)) {
		csv_cma = (struct csv_cma *)page->private;
		WARN_ON(!csv_cma);
		if (likely(csv_cma)) {
			page->private = 0;
			csv_cma->fast = 1;
			cma_release(csv_cma->cma, page, PAGE_ALIGN(size) >> PAGE_SHIFT);
		}
	}
}
EXPORT_SYMBOL_GPL(csv_release_to_contiguous);

#else /* !CONFIG_CMA */

unsigned int csv_get_smr_entry_shift(void)
{
	return 0;
}
EXPORT_SYMBOL_GPL(csv_get_smr_entry_shift);

void __init early_csv_reserve_mem(void)
{
	/* Only reserve memory on the host that enabled CSV3 feature */
	if (csv3_check_cpu_support())
		pr_warn("CSV-CMA: CONFIG_CMA=n, memory for CSV3 unavailable!\n");
}

phys_addr_t csv_alloc_from_contiguous(size_t size, nodemask_t *nodes_allowed,
				      unsigned int align)
{
	return 0;
}
EXPORT_SYMBOL_GPL(csv_alloc_from_contiguous);

void csv_release_to_contiguous(phys_addr_t pa, size_t size)
{
}
EXPORT_SYMBOL_GPL(csv_release_to_contiguous);

#endif /* CONFIG_CMA */
+109 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#include <uapi/linux/psp-hygon.h>
#include <linux/bitfield.h>

#include <asm/csv.h>

#include "psp-dev.h"
#include "csv-dev.h"
#include "ring-buffer.h"
@@ -61,6 +63,15 @@ int csv_cmd_buffer_len(int cmd)
	switch (cmd) {
	case CSV_CMD_HGSC_CERT_IMPORT:		return sizeof(struct csv_data_hgsc_cert_import);
	case CSV_CMD_RING_BUFFER:		return sizeof(struct csv_data_ring_buffer);
	case CSV3_CMD_LAUNCH_ENCRYPT_DATA:	return sizeof(struct csv3_data_launch_encrypt_data);
	case CSV3_CMD_LAUNCH_ENCRYPT_VMCB:	return sizeof(struct csv3_data_launch_encrypt_vmcb);
	case CSV3_CMD_UPDATE_NPT:		return sizeof(struct csv3_data_update_npt);
	case CSV3_CMD_SET_SMR:			return sizeof(struct csv3_data_set_smr);
	case CSV3_CMD_SET_SMCR:			return sizeof(struct csv3_data_set_smcr);
	case CSV3_CMD_SET_GUEST_PRIVATE_MEMORY:
					return sizeof(struct csv3_data_set_guest_private_memory);
	case CSV3_CMD_DBG_READ_VMSA:		return sizeof(struct csv3_data_dbg_read_vmsa);
	case CSV3_CMD_DBG_READ_MEM:		return sizeof(struct csv3_data_dbg_read_mem);
	default:				return 0;
	}
}
@@ -597,6 +608,104 @@ int csv_check_stat_queue_status(int *psp_ret)
}
EXPORT_SYMBOL_GPL(csv_check_stat_queue_status);

#ifdef CONFIG_HYGON_CSV

int csv_platform_cmd_set_secure_memory_region(struct sev_device *sev, int *error)
{
	int ret = 0;
	unsigned int i = 0;
	struct csv3_data_set_smr *cmd_set_smr;
	struct csv3_data_set_smcr *cmd_set_smcr;
	struct csv3_data_memory_region *smr_regions;

	if (!hygon_psp_hooks.sev_dev_hooks_installed) {
		ret = -ENODEV;
		goto l_end;
	}

	if (!csv_smr || !csv_smr_num) {
		ret = -EINVAL;
		goto l_end;
	}

	cmd_set_smr = kzalloc(sizeof(*cmd_set_smr), GFP_KERNEL);
	if (!cmd_set_smr) {
		ret = -ENOMEM;
		goto l_end;
	}

	smr_regions = kcalloc(csv_smr_num, sizeof(*smr_regions),  GFP_KERNEL);
	if (!smr_regions) {
		ret = -ENOMEM;
		goto e_free_cmd_set_smr;
	}

	for (i = 0; i < csv_smr_num; i++) {
		smr_regions[i].base_address = csv_smr[i].start;
		smr_regions[i].size = csv_smr[i].size;
	}
	cmd_set_smr->smr_entry_size = 1 << csv_get_smr_entry_shift();
	cmd_set_smr->regions_paddr = __psp_pa(smr_regions);
	cmd_set_smr->nregions = csv_smr_num;
	ret = hygon_psp_hooks.sev_do_cmd(CSV3_CMD_SET_SMR, cmd_set_smr, error);
	if (ret) {
		pr_err("Fail to set SMR, ret %#x, error %#x\n", ret, *error);
		goto e_free_smr_area;
	}

	cmd_set_smcr = kzalloc(sizeof(*cmd_set_smcr), GFP_KERNEL);
	if (!cmd_set_smcr) {
		ret = -ENOMEM;
		goto e_free_smr_area;
	}

	cmd_set_smcr->base_address = csv_alloc_from_contiguous(1UL << CSV_MR_ALIGN_BITS,
						&node_online_map,
						get_order(1 << CSV_MR_ALIGN_BITS));
	if (!cmd_set_smcr->base_address) {
		pr_err("Fail to alloc SMCR memory\n");
		ret = -ENOMEM;
		goto e_free_cmd_set_smcr;
	}

	cmd_set_smcr->size = 1UL << CSV_MR_ALIGN_BITS;
	ret = hygon_psp_hooks.sev_do_cmd(CSV3_CMD_SET_SMCR, cmd_set_smcr, error);
	if (ret) {
		if (*error == SEV_RET_INVALID_COMMAND)
			ret = 0;
		else
			pr_err("set smcr ret %#x, error %#x\n", ret, *error);

		csv_release_to_contiguous(cmd_set_smcr->base_address,
					1UL << CSV_MR_ALIGN_BITS);
	}

e_free_cmd_set_smcr:
	kfree((void *)cmd_set_smcr);
e_free_smr_area:
	kfree((void *)smr_regions);
e_free_cmd_set_smr:
	kfree((void *)cmd_set_smr);

l_end:
	if (ret)
		dev_warn(sev->dev,
			 "CSV3: fail to set secure memory region, CSV3 support unavailable\n");

	return ret;
}

#else	/* !CONFIG_HYGON_CSV */

int csv_platform_cmd_set_secure_memory_region(struct sev_device *sev, int *error)
{
	dev_warn(sev->dev,
		 "CSV3: needs CONFIG_HYGON_CSV, CSV3 support unavailable\n");
	return -EFAULT;
}

#endif	/* CONFIG_HYGON_CSV */

static int get_queue_tail(struct csv_ringbuffer_queue *ringbuffer)
{
	return ringbuffer->cmd_ptr.tail & ringbuffer->cmd_ptr.mask;
Loading