--- v1
+++ v4
@@ -1,233 +1,133 @@
From: Tianyu Lan <Tianyu.Lan@microsoft.com>
-Add new hvcall guest address host visibility support to mark
-memory visible to host. Call it inside set_memory_decrypted
-/encrypted().
+Mark vmbus ring buffer visible with set_memory_decrypted() when
+establish gpadl handle.
Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
---
- arch/x86/hyperv/Makefile | 2 +-
- arch/x86/hyperv/ivm.c | 112 +++++++++++++++++++++++++++++
- arch/x86/include/asm/hyperv-tlfs.h | 18 +++++
- arch/x86/include/asm/mshyperv.h | 3 +-
- arch/x86/mm/pat/set_memory.c | 6 +-
- include/asm-generic/hyperv-tlfs.h | 1 +
- 6 files changed, 139 insertions(+), 3 deletions(-)
- create mode 100644 arch/x86/hyperv/ivm.c
+ drivers/hv/channel.c | 38 ++++++++++++++++++++++++++++++++++++--
+ include/linux/hyperv.h | 10 ++++++++++
+ 2 files changed, 46 insertions(+), 2 deletions(-)
-diff --git a/arch/x86/hyperv/Makefile b/arch/x86/hyperv/Makefile
-index 48e2c51464e8..5d2de10809ae 100644
---- a/arch/x86/hyperv/Makefile
-+++ b/arch/x86/hyperv/Makefile
-@@ -1,5 +1,5 @@
- # 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
+diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
+index f3761c73b074..01048bb07082 100644
+--- a/drivers/hv/channel.c
++++ b/drivers/hv/channel.c
+@@ -17,6 +17,7 @@
+ #include <linux/hyperv.h>
+ #include <linux/uio.h>
+ #include <linux/interrupt.h>
++#include <linux/set_memory.h>
+ #include <asm/page.h>
+ #include <asm/mshyperv.h>
- ifdef CONFIG_X86_64
-diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c
-new file mode 100644
-index 000000000000..24a58795abd8
---- /dev/null
-+++ b/arch/x86/hyperv/ivm.c
-@@ -0,0 +1,112 @@
-+// 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 encripted from host and guest
-+ * needs to set memory visible to host via hvcall before sharing memory
-+ * with host.
-+ */
-+int hv_mark_gpa_visibility(u16 count, const u64 pfn[], u32 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;
+@@ -465,7 +466,7 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
+ struct list_head *curr;
+ u32 next_gpadl_handle;
+ unsigned long flags;
+- int ret = 0;
++ int ret = 0, index;
+
+ next_gpadl_handle =
+ (atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
+@@ -474,6 +475,13 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
+ if (ret)
+ return ret;
+
++ ret = set_memory_decrypted((unsigned long)kbuffer,
++ HVPFN_UP(size));
++ if (ret) {
++ pr_warn("Failed to set host visibility.\n");
++ return ret;
+ }
+
-+ 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;
-+ }
+ init_completion(&msginfo->waitevent);
+ msginfo->waiting_channel = channel;
+
+@@ -539,6 +547,15 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
+ /* At this point, we received the gpadl created msg */
+ *gpadl_handle = gpadlmsg->gpadl;
+
++ if (type == HV_GPADL_BUFFER)
++ index = 0;
++ else
++ index = channel->gpadl_range[1].gpadlhandle ? 2 : 1;
+
-+ 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);
++ channel->gpadl_range[index].size = size;
++ channel->gpadl_range[index].buffer = kbuffer;
++ channel->gpadl_range[index].gpadlhandle = *gpadl_handle;
+
-+ if (!(hv_status & HV_HYPERCALL_RESULT_MASK))
-+ return 0;
+ cleanup:
+ spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
+ list_del(&msginfo->msglistentry);
+@@ -549,6 +566,11 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
+ }
+
+ kfree(msginfo);
+
-+ return hv_status & HV_HYPERCALL_RESULT_MASK;
-+}
-+EXPORT_SYMBOL(hv_mark_gpa_visibility);
++ if (ret)
++ set_memory_encrypted((unsigned long)kbuffer,
++ HVPFN_UP(size));
+
-+/*
-+ * 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.
-+ */
-+static int hv_set_mem_host_visibility(void *kbuffer, size_t size, u32 visibility)
-+{
-+ int pagecount = size >> HV_HYP_PAGE_SHIFT;
-+ u64 *pfn_array;
-+ int ret = 0;
-+ int i, pfn;
+ return ret;
+ }
+
+@@ -811,7 +833,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
+ struct vmbus_channel_gpadl_teardown *msg;
+ struct vmbus_channel_msginfo *info;
+ unsigned long flags;
+- int ret;
++ int ret, i;
+
+ info = kzalloc(sizeof(*info) +
+ sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL);
+@@ -859,6 +881,18 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
+ spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
+
+ kfree(info);
+
-+ if (!hv_is_isolation_supported() || !ms_hyperv.ghcb_base)
-+ return 0;
++ /* Find gpadl buffer virtual address and size. */
++ for (i = 0; i < VMBUS_GPADL_RANGE_COUNT; i++)
++ if (channel->gpadl_range[i].gpadlhandle == gpadl_handle)
++ break;
+
-+ pfn_array = kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
-+ if (!pfn_array)
-+ return -ENOMEM;
++ if (set_memory_encrypted((unsigned long)channel->gpadl_range[i].buffer,
++ HVPFN_UP(channel->gpadl_range[i].size)))
++ pr_warn("Fail to set mem host visibility.\n");
+
-+ for (i = 0, pfn = 0; i < pagecount; i++) {
-+ pfn_array[pfn] = virt_to_hvpfn(kbuffer + i * HV_HYP_PAGE_SIZE);
-+ pfn++;
++ channel->gpadl_range[i].gpadlhandle = 0;
+
-+ if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) {
-+ ret |= hv_mark_gpa_visibility(pfn, pfn_array, visibility);
-+ pfn = 0;
+ return ret;
+ }
+ EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
+diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
+index 2e859d2f9609..06eccaba10c5 100644
+--- a/include/linux/hyperv.h
++++ b/include/linux/hyperv.h
+@@ -809,6 +809,14 @@ struct vmbus_device {
+
+ #define VMBUS_DEFAULT_MAX_PKT_SIZE 4096
+
++struct vmbus_gpadl_range {
++ u32 gpadlhandle;
++ u32 size;
++ void *buffer;
++};
+
-+ if (ret)
-+ goto err_free_pfn_array;
-+ }
-+ }
++#define VMBUS_GPADL_RANGE_COUNT 3
+
-+ err_free_pfn_array:
-+ kfree(pfn_array);
-+ return ret;
-+}
-+
-+int hv_set_mem_enc(unsigned long addr, int numpages, bool enc)
-+{
-+ return hv_set_mem_host_visibility((void *)addr,
-+ numpages * HV_HYP_PAGE_SIZE,
-+ enc ? VMBUS_PAGE_NOT_VISIBLE
-+ : VMBUS_PAGE_VISIBLE_READ_WRITE);
-+}
-diff --git a/arch/x86/include/asm/hyperv-tlfs.h b/arch/x86/include/asm/hyperv-tlfs.h
-index f1366ce609e3..f027b5bf6076 100644
---- a/arch/x86/include/asm/hyperv-tlfs.h
-+++ b/arch/x86/include/asm/hyperv-tlfs.h
-@@ -276,6 +276,11 @@ 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
+ struct vmbus_channel {
+ struct list_head listentry;
-+/* Hyper-V GPA map flags */
-+#define VMBUS_PAGE_NOT_VISIBLE 0
-+#define VMBUS_PAGE_VISIBLE_READ_ONLY 1
-+#define VMBUS_PAGE_VISIBLE_READ_WRITE 3
-+
- /*
- * Declare the MSR used to setup pages used to communicate with the hypervisor.
- */
-@@ -578,4 +583,17 @@ enum hv_interrupt_type {
+@@ -829,6 +837,8 @@ struct vmbus_channel {
+ struct completion rescind_event;
- #include <asm-generic/hyperv-tlfs.h>
+ u32 ringbuffer_gpadlhandle;
++ /* GPADL_RING and Send/Receive GPADL_BUFFER. */
++ struct vmbus_gpadl_range gpadl_range[VMBUS_GPADL_RANGE_COUNT];
-+/* All input parameters should be in single page. */
-+#define HV_MAX_MODIFY_GPA_REP_COUNT \
-+ ((PAGE_SIZE / sizeof(u64)) - 2)
-+
-+/* HvCallModifySparseGpaPageHostVisibility hypercall */
-+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;
-+
- #endif
-diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
-index 6627cfd2bfba..68dd207c2603 100644
---- a/arch/x86/include/asm/mshyperv.h
-+++ b/arch/x86/include/asm/mshyperv.h
-@@ -190,7 +190,8 @@ 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_mark_gpa_visibility(u16 count, const u64 pfn[], u32 visibility);
-+int hv_set_mem_enc(unsigned long addr, int numpages, bool enc);
- #else /* CONFIG_HYPERV */
- static inline void hyperv_init(void) {}
- static inline void hyperv_setup_mmu_ops(void) {}
-diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
-index ad8a5c586a35..ba2a22886976 100644
---- a/arch/x86/mm/pat/set_memory.c
-+++ b/arch/x86/mm/pat/set_memory.c
-@@ -29,6 +29,8 @@
- #include <asm/proto.h>
- #include <asm/memtype.h>
- #include <asm/set_memory.h>
-+#include <asm/hyperv-tlfs.h>
-+#include <asm/mshyperv.h>
-
- #include "../mm_internal.h"
-
-@@ -1986,7 +1988,9 @@ static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
- int ret;
-
- /* Nothing to do if memory encryption is not active */
-- if (!mem_encrypt_active())
-+ if (hv_is_isolation_supported())
-+ return hv_set_mem_enc(addr, numpages, enc);
-+ else if (!mem_encrypt_active())
- return 0;
-
- /* Should not be working on unaligned addresses */
-diff --git a/include/asm-generic/hyperv-tlfs.h b/include/asm-generic/hyperv-tlfs.h
-index 56348a541c50..8ed6733d5146 100644
---- a/include/asm-generic/hyperv-tlfs.h
-+++ b/include/asm-generic/hyperv-tlfs.h
-@@ -158,6 +158,7 @@ struct ms_hyperv_tsc_page {
- #define HVCALL_RETARGET_INTERRUPT 0x007e
- #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
- #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
-+#define HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY 0x00db
-
- /* Extended hypercalls */
- #define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001
+ /* Allocated memory for ring buffer */
+ struct page *ringbuffer_page;
--
2.25.1