Commit 810a5212 authored by Tianyu Lan's avatar Tianyu Lan Committed by Wei Liu
Browse files

x86/hyperv: Add new hvcall guest address host visibility support



Add new hvcall guest address host visibility support to mark
memory visible to host. Call it inside set_memory_decrypted
/encrypted(). Add HYPERVISOR feature check in the
hv_is_isolation_supported() to optimize in non-virtualization
environment.

Acked-by: default avatarDave Hansen <dave.hansen@intel.com>
Reviewed-by: default avatarMichael Kelley <mikelley@microsoft.com>
Signed-off-by: default avatarTianyu Lan <Tianyu.Lan@microsoft.com>
Link: https://lore.kernel.org/r/20211025122116.264793-4-ltykernel@gmail.com


[ wei: fix conflicts with tip ]
Signed-off-by: default avatarWei Liu <wei.liu@kernel.org>
parent af788f35
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
obj-y			:= hv_init.o mmu.o nested.o irqdomain.o
obj-y			:= hv_init.o mmu.o nested.o irqdomain.o ivm.o
obj-$(CONFIG_X86_64)	+= hv_apic.o hv_proc.o

ifdef CONFIG_X86_64
+6 −0
Original line number Diff line number Diff line
@@ -603,6 +603,12 @@ EXPORT_SYMBOL_GPL(hv_get_isolation_type);

bool hv_is_isolation_supported(void)
{
	if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
		return false;

	if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
		return false;

	return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
}

arch/x86/hyperv/ivm.c

0 → 100644
+105 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Hyper-V Isolation VM interface with paravisor and hypervisor
 *
 * Author:
 *  Tianyu Lan <Tianyu.Lan@microsoft.com>
 */

#include <linux/hyperv.h>
#include <linux/types.h>
#include <linux/bitfield.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/mshyperv.h>

/*
 * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
 *
 * In Isolation VM, all guest memory is encrypted from host and guest
 * needs to set memory visible to host via hvcall before sharing memory
 * with host.
 */
static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
			   enum hv_mem_host_visibility visibility)
{
	struct hv_gpa_range_for_visibility **input_pcpu, *input;
	u16 pages_processed;
	u64 hv_status;
	unsigned long flags;

	/* no-op if partition isolation is not enabled */
	if (!hv_is_isolation_supported())
		return 0;

	if (count > HV_MAX_MODIFY_GPA_REP_COUNT) {
		pr_err("Hyper-V: GPA count:%d exceeds supported:%lu\n", count,
			HV_MAX_MODIFY_GPA_REP_COUNT);
		return -EINVAL;
	}

	local_irq_save(flags);
	input_pcpu = (struct hv_gpa_range_for_visibility **)
			this_cpu_ptr(hyperv_pcpu_input_arg);
	input = *input_pcpu;
	if (unlikely(!input)) {
		local_irq_restore(flags);
		return -EINVAL;
	}

	input->partition_id = HV_PARTITION_ID_SELF;
	input->host_visibility = visibility;
	input->reserved0 = 0;
	input->reserved1 = 0;
	memcpy((void *)input->gpa_page_list, pfn, count * sizeof(*pfn));
	hv_status = hv_do_rep_hypercall(
			HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY, count,
			0, input, &pages_processed);
	local_irq_restore(flags);

	if (hv_result_success(hv_status))
		return 0;
	else
		return -EFAULT;
}

/*
 * hv_set_mem_host_visibility - Set specified memory visible to host.
 *
 * In Isolation VM, all guest memory is encrypted from host and guest
 * needs to set memory visible to host via hvcall before sharing memory
 * with host. This function works as wrap of hv_mark_gpa_visibility()
 * with memory base and size.
 */
int hv_set_mem_host_visibility(unsigned long kbuffer, int pagecount, bool visible)
{
	enum hv_mem_host_visibility visibility = visible ?
			VMBUS_PAGE_VISIBLE_READ_WRITE : VMBUS_PAGE_NOT_VISIBLE;
	u64 *pfn_array;
	int ret = 0;
	int i, pfn;

	if (!hv_is_isolation_supported() || !hv_hypercall_pg)
		return 0;

	pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
	if (!pfn_array)
		return -ENOMEM;

	for (i = 0, pfn = 0; i < pagecount; i++) {
		pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE);
		pfn++;

		if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) {
			ret = hv_mark_gpa_visibility(pfn, pfn_array,
						     visibility);
			if (ret)
				goto err_free_pfn_array;
			pfn = 0;
		}
	}

 err_free_pfn_array:
	kfree(pfn_array);
	return ret;
}
+17 −0
Original line number Diff line number Diff line
@@ -276,6 +276,23 @@ enum hv_isolation_type {
#define HV_X64_MSR_TIME_REF_COUNT	HV_REGISTER_TIME_REF_COUNT
#define HV_X64_MSR_REFERENCE_TSC	HV_REGISTER_REFERENCE_TSC

/* Hyper-V memory host visibility */
enum hv_mem_host_visibility {
	VMBUS_PAGE_NOT_VISIBLE		= 0,
	VMBUS_PAGE_VISIBLE_READ_ONLY	= 1,
	VMBUS_PAGE_VISIBLE_READ_WRITE	= 3
};

/* HvCallModifySparseGpaPageHostVisibility hypercall */
#define HV_MAX_MODIFY_GPA_REP_COUNT	((PAGE_SIZE / sizeof(u64)) - 2)
struct hv_gpa_range_for_visibility {
	u64 partition_id;
	u32 host_visibility:2;
	u32 reserved0:30;
	u32 reserved1;
	u64 gpa_page_list[HV_MAX_MODIFY_GPA_REP_COUNT];
} __packed;

/*
 * Declare the MSR used to setup pages used to communicate with the hypervisor.
 */
+6 −1
Original line number Diff line number Diff line
@@ -192,7 +192,7 @@ struct irq_domain *hv_create_pci_msi_domain(void);
int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector,
		struct hv_interrupt_entry *entry);
int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);

int hv_set_mem_host_visibility(unsigned long addr, int numpages, bool visible);
#else /* CONFIG_HYPERV */
static inline void hyperv_init(void) {}
static inline void hyperv_setup_mmu_ops(void) {}
@@ -209,6 +209,11 @@ static inline int hyperv_flush_guest_mapping_range(u64 as,
{
	return -1;
}
static inline int hv_set_mem_host_visibility(unsigned long addr, int numpages,
					     bool visible)
{
	return -1;
}
#endif /* CONFIG_HYPERV */


Loading