From: "Mickaël Salaün" <mic@digikod.net>
To: Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
"H . Peter Anvin" <hpa@zytor.com>, Ingo Molnar <mingo@redhat.com>,
Kees Cook <keescook@chromium.org>,
Paolo Bonzini <pbonzini@redhat.com>,
Sean Christopherson <seanjc@google.com>,
Thomas Gleixner <tglx@linutronix.de>,
Vitaly Kuznetsov <vkuznets@redhat.com>,
Wanpeng Li <wanpengli@tencent.com>
Cc: "Mickaël Salaün" <mic@digikod.net>,
"Alexander Graf" <graf@amazon.com>,
"Chao Peng" <chao.p.peng@linux.intel.com>,
"Edgecombe, Rick P" <rick.p.edgecombe@intel.com>,
"Forrest Yuan Yu" <yuanyu@google.com>,
"James Gowans" <jgowans@amazon.com>,
"James Morris" <jamorris@linux.microsoft.com>,
"John Andersen" <john.s.andersen@intel.com>,
"Madhavan T . Venkataraman" <madvenka@linux.microsoft.com>,
"Marian Rotariu" <marian.c.rotariu@gmail.com>,
"Mihai Donțu" <mdontu@bitdefender.com>,
"Nicușor Cîțu" <nicu.citu@icloud.com>,
"Thara Gopinath" <tgopinath@microsoft.com>,
"Trilok Soni" <quic_tsoni@quicinc.com>,
"Wei Liu" <wei.liu@kernel.org>, "Will Deacon" <will@kernel.org>,
"Yu Zhang" <yu.c.zhang@linux.intel.com>,
"Zahra Tarkhani" <ztarkhani@microsoft.com>,
"Ștefan Șicleru" <ssicleru@bitdefender.com>,
dev@lists.cloudhypervisor.org, kvm@vger.kernel.org,
linux-hardening@vger.kernel.org, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org, qemu-devel@nongnu.org,
virtualization@lists.linux-foundation.org, x86@kernel.org,
xen-devel@lists.xenproject.org
Subject: [RFC PATCH v2 18/19] heki: x86: Protect guest kernel memory using the KVM hypervisor
Date: Sun, 12 Nov 2023 21:23:25 -0500 [thread overview]
Message-ID: <20231113022326.24388-19-mic@digikod.net> (raw)
In-Reply-To: <20231113022326.24388-1-mic@digikod.net>
From: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Implement a hypervisor function, kvm_protect_memory() that calls the
KVM_HC_PROTECT_MEMORY hypercall to request the KVM hypervisor to
set specified permissions on a list of guest pages.
Using the protect_memory() function, set proper EPT permissions for all
guest pages.
Use the MEM_ATTR_IMMUTABLE property to protect the kernel static
sections and the boot-time read-only sections. This enables to make sure
a compromised guest will not be able to change its main physical memory
page permissions. However, this also disable any feature that may change
the kernel's text section (e.g., ftrace, Kprobes), but they can still be
used on kernel modules.
Module loading/unloading, and eBPF JIT is allowed without restrictions
for now, but we'll need a way to authenticate these code changes to
really improve the guests' security. We plan to use module signatures,
but there is no solution yet to authenticate eBPF programs.
Being able to use ftrace and Kprobes in a secure way is a challenge not
solved yet. We're looking for ideas to make this work.
Likewise, the JUMP_LABEL feature cannot work because the kernel's text
section is read-only.
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: Wanpeng Li <wanpengli@tencent.com>
Co-developed-by: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
---
Changes since v1:
* New patch
---
arch/x86/kernel/kvm.c | 11 ++++++
arch/x86/kvm/mmu/mmu.c | 2 +-
arch/x86/mm/heki.c | 21 ++++++++++
include/linux/heki.h | 26 ++++++++++++
virt/heki/Kconfig | 1 +
virt/heki/counters.c | 90 ++++++++++++++++++++++++++++++++++++++++--
virt/heki/main.c | 83 +++++++++++++++++++++++++++++++++++++-
7 files changed, 229 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 8349f4ad3bbd..343615b0e3bf 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -1021,8 +1021,19 @@ static int kvm_lock_crs(void)
return err;
}
+static int kvm_protect_memory(gpa_t pa)
+{
+ long err;
+
+ WARN_ON_ONCE(in_interrupt());
+
+ err = kvm_hypercall1(KVM_HC_PROTECT_MEMORY, pa);
+ return err;
+}
+
static struct heki_hypervisor kvm_heki_hypervisor = {
.lock_crs = kvm_lock_crs,
+ .protect_memory = kvm_protect_memory,
};
static void kvm_init_heki(void)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 2d09bcc35462..13be05e9ccf1 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -7374,7 +7374,7 @@ bool kvm_arch_post_set_memory_attributes(struct kvm *kvm,
int level;
lockdep_assert_held_write(&kvm->mmu_lock);
- lockdep_assert_held(&kvm->slots_lock);
+ lockdep_assert_held(&kvm->slots_arch_lock);
/*
* The sequence matters here: upper levels consume the result of lower
diff --git a/arch/x86/mm/heki.c b/arch/x86/mm/heki.c
index e4c60d8b4f2d..6c3fa9defada 100644
--- a/arch/x86/mm/heki.c
+++ b/arch/x86/mm/heki.c
@@ -45,6 +45,19 @@ __init void heki_arch_early_init(void)
heki_map(direct_map_end, kernel_end);
}
+void heki_arch_late_init(void)
+{
+ /*
+ * The permission counters for all existing kernel mappings have
+ * already been updated. Now, walk all the pages, compute their
+ * permissions from the counters and apply the permissions in the
+ * host page table. To accomplish this, we walk the direct map
+ * range.
+ */
+ heki_protect(direct_map_va, direct_map_end);
+ pr_warn("Guest memory protected\n");
+}
+
unsigned long heki_flags_to_permissions(unsigned long flags)
{
unsigned long permissions;
@@ -67,6 +80,11 @@ void heki_pgprot_to_permissions(pgprot_t prot, unsigned long *set,
*clear |= MEM_ATTR_EXEC;
}
+unsigned long heki_default_permissions(void)
+{
+ return MEM_ATTR_READ | MEM_ATTR_WRITE;
+}
+
static unsigned long heki_pgprot_to_flags(pgprot_t prot)
{
unsigned long flags = 0;
@@ -100,6 +118,9 @@ static void heki_text_poke_common(struct page **pages, int npages,
heki_callback(&args);
}
+ if (args.head)
+ heki_apply_permissions(&args);
+
mutex_unlock(&heki_lock);
}
diff --git a/include/linux/heki.h b/include/linux/heki.h
index 6f2cfddc6dac..306bcec7ae92 100644
--- a/include/linux/heki.h
+++ b/include/linux/heki.h
@@ -15,6 +15,8 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/printk.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
#include <linux/slab.h>
#ifdef CONFIG_HEKI
@@ -61,6 +63,7 @@ struct heki_page_list {
*/
struct heki_hypervisor {
int (*lock_crs)(void); /* Lock control registers. */
+ int (*protect_memory)(gpa_t pa); /* Protect guest memory */
};
/*
@@ -74,16 +77,28 @@ struct heki_hypervisor {
* - a page is mapped into the kernel address space
* - a page is unmapped from the kernel address space
* - permissions are changed for a mapped page
+ *
+ * At the end of kernel boot (before kicking off the init process), the
+ * permissions for guest pages are applied to the host page table.
+ *
+ * Beyond that point, the counters and host page table permissions are updated
+ * whenever:
+ *
+ * - a guest page is mapped into the kernel address space
+ * - a guest page is unmapped from the kernel address space
+ * - permissions are changed for a mapped guest page
*/
struct heki {
struct heki_hypervisor *hypervisor;
struct mem_table *counters;
+ bool protect_memory;
};
enum heki_cmd {
HEKI_MAP,
HEKI_UPDATE,
HEKI_UNMAP,
+ HEKI_PROTECT_MEMORY,
};
/*
@@ -103,7 +118,12 @@ struct heki_args {
/* Permissions passed by heki_update(). */
unsigned long set;
+ unsigned long set_global;
unsigned long clear;
+
+ /* Page list is built by the callback. */
+ struct heki_page_list *head;
+ phys_addr_t head_pa;
};
/* Callback function called by the table walker. */
@@ -125,14 +145,20 @@ void heki_update(unsigned long va, unsigned long end, unsigned long set,
unsigned long clear);
void heki_unmap(unsigned long va, unsigned long end);
void heki_callback(struct heki_args *args);
+void heki_protect(unsigned long va, unsigned long end);
+void heki_add_pa(struct heki_args *args, phys_addr_t pa,
+ unsigned long permissions);
+void heki_apply_permissions(struct heki_args *args);
/* Arch-specific functions. */
void heki_arch_early_init(void);
+void heki_arch_late_init(void);
unsigned long heki_flags_to_permissions(unsigned long flags);
void heki_pgprot_to_permissions(pgprot_t prot, unsigned long *set,
unsigned long *clear);
void heki_text_poke_start(struct page **pages, int npages, pgprot_t prot);
void heki_text_poke_end(struct page **pages, int npages, pgprot_t prot);
+unsigned long heki_default_permissions(void);
#else /* !CONFIG_HEKI */
diff --git a/virt/heki/Kconfig b/virt/heki/Kconfig
index 6d956eb9d04b..9bde84cd759e 100644
--- a/virt/heki/Kconfig
+++ b/virt/heki/Kconfig
@@ -8,6 +8,7 @@ config HEKI
select KVM_GENERIC_MEMORY_ATTRIBUTES
depends on !X86_16BIT
select SPARSEMEM
+ depends on !JUMP_LABEL
help
This feature enhances guest virtual machine security by taking
advantage of security features provided by the hypervisor for guests.
diff --git a/virt/heki/counters.c b/virt/heki/counters.c
index d0f830b0775a..302113bee6ff 100644
--- a/virt/heki/counters.c
+++ b/virt/heki/counters.c
@@ -13,6 +13,25 @@
DEFINE_MUTEX(heki_lock);
+static inline unsigned long heki_permissions(struct heki_counters *counters)
+{
+ unsigned long permissions;
+
+ if (!counters)
+ return heki_default_permissions();
+
+ permissions = 0;
+ if (counters->read)
+ permissions |= MEM_ATTR_READ;
+ if (counters->write)
+ permissions |= MEM_ATTR_WRITE;
+ if (counters->execute)
+ permissions |= MEM_ATTR_EXEC;
+ if (!permissions)
+ permissions = heki_default_permissions();
+ return permissions;
+}
+
static void heki_update_counters(struct heki_counters *counters,
unsigned long perm, unsigned long set,
unsigned long clear)
@@ -53,20 +72,38 @@ static struct heki_counters *heki_create_counters(struct mem_table *table,
return counters;
}
+static void heki_check_counters(struct heki_counters *counters,
+ unsigned long permissions)
+{
+ /*
+ * If a permission has been added to a PTE directly, it will not be
+ * reflected in the counters. Adjust for that. This is a bit of a
+ * hack, really.
+ */
+ if ((permissions & MEM_ATTR_READ) && !counters->read)
+ counters->read++;
+ if ((permissions & MEM_ATTR_WRITE) && !counters->write)
+ counters->write++;
+ if ((permissions & MEM_ATTR_EXEC) && !counters->execute)
+ counters->execute++;
+}
+
void heki_callback(struct heki_args *args)
{
/* The VA is only for debug. It is not really used in this function. */
unsigned long va;
phys_addr_t pa, pa_end;
- unsigned long permissions;
+ unsigned long permissions, existing, new;
void **entry;
struct heki_counters *counters;
unsigned int ignore;
+ bool protect_memory;
if (!pfn_valid(args->pa >> PAGE_SHIFT))
return;
permissions = heki_flags_to_permissions(args->flags);
+ protect_memory = heki.protect_memory;
/*
* Handle counters for a leaf entry in the kernel page table.
@@ -80,6 +117,8 @@ void heki_callback(struct heki_args *args)
else
counters = NULL;
+ existing = heki_permissions(counters);
+
switch (args->cmd) {
case HEKI_MAP:
if (!counters)
@@ -102,10 +141,30 @@ void heki_callback(struct heki_args *args)
permissions);
break;
+ case HEKI_PROTECT_MEMORY:
+ if (counters)
+ heki_check_counters(counters, permissions);
+ existing = 0;
+ break;
+
default:
WARN_ON_ONCE(1);
break;
}
+
+ new = heki_permissions(counters) | args->set_global;
+
+ /*
+ * To be able to use a pool of allocated memory for new
+ * executable or read-only mappings (e.g., kernel module
+ * loading), ignores immutable attribute if memory can be
+ * changed.
+ */
+ if (new & MEM_ATTR_WRITE)
+ new &= ~MEM_ATTR_IMMUTABLE;
+
+ if (protect_memory && existing != new)
+ heki_add_pa(args, pa, new);
}
}
@@ -120,14 +179,20 @@ static void heki_func(unsigned long va, unsigned long end,
mutex_lock(&heki_lock);
+ if (args->cmd == HEKI_PROTECT_MEMORY)
+ heki.protect_memory = true;
+
heki_walk(va, end, heki_callback, args);
+ if (args->head)
+ heki_apply_permissions(args);
+
mutex_unlock(&heki_lock);
}
/*
* Find the mappings in the given range and initialize permission counters for
- * them.
+ * them. Apply permissions in the host page table.
*/
void heki_map(unsigned long va, unsigned long end)
{
@@ -138,6 +203,25 @@ void heki_map(unsigned long va, unsigned long end)
heki_func(va, end, &args);
}
+/*
+ * The architecture calls this to protect all guest pages at the end of
+ * kernel init. Up to this point, only the counters for guest pages have been
+ * updated. No permissions have been applied on the host page table.
+ * Now, the permissions will be applied.
+ *
+ * Beyond this point, the host page table permissions will always be updated
+ * whenever the counters are updated.
+ */
+void heki_protect(unsigned long va, unsigned long end)
+{
+ struct heki_args args = {
+ .cmd = HEKI_PROTECT_MEMORY,
+ .set_global = MEM_ATTR_IMMUTABLE,
+ };
+
+ heki_func(va, end, &args);
+}
+
/*
* Find the mappings in the given range and update permission counters for
* them. Apply permissions in the host page table.
@@ -156,7 +240,7 @@ void heki_update(unsigned long va, unsigned long end, unsigned long set,
/*
* Find the mappings in the given range and revert the permission counters for
- * them.
+ * them. Apply permissions in the host page table.
*/
void heki_unmap(unsigned long va, unsigned long end)
{
diff --git a/virt/heki/main.c b/virt/heki/main.c
index 0ab7de659e6f..5629334112e7 100644
--- a/virt/heki/main.c
+++ b/virt/heki/main.c
@@ -51,7 +51,7 @@ void heki_late_init(void)
{
struct heki_hypervisor *hypervisor = heki.hypervisor;
- if (!heki_enabled || !heki.hypervisor)
+ if (!heki.counters)
return;
/* Locks control registers so a compromised guest cannot change them. */
@@ -59,6 +59,87 @@ void heki_late_init(void)
return;
pr_warn("Control registers locked\n");
+
+ heki_arch_late_init();
+}
+
+/*
+ * Build a list of guest pages with their permissions. This list will be
+ * passed to the VMM/Hypervisor to set these permissions in the host page
+ * table.
+ */
+void heki_add_pa(struct heki_args *args, phys_addr_t pa,
+ unsigned long permissions)
+{
+ struct heki_page_list *list = args->head;
+ struct heki_pages *hpage;
+ u64 max_pages;
+ struct page *page;
+ bool new = false;
+
+ max_pages = (PAGE_SIZE - sizeof(*list)) / sizeof(*hpage);
+again:
+ if (!list || list->npages == max_pages) {
+ page = alloc_page(GFP_KERNEL);
+ if (WARN_ON_ONCE(!page))
+ return;
+
+ list = page_address(page);
+ list->npages = 0;
+ list->next_pa = args->head_pa;
+ list->next = args->head;
+
+ args->head = list;
+ args->head_pa = page_to_pfn(page) << PAGE_SHIFT;
+ new = true;
+ }
+
+ hpage = &list->pages[list->npages];
+ if (new) {
+ hpage->pa = pa;
+ hpage->epa = pa + PAGE_SIZE;
+ hpage->permissions = permissions;
+ return;
+ }
+
+ if (pa == hpage->epa && permissions == hpage->permissions) {
+ hpage->epa += PAGE_SIZE;
+ return;
+ }
+
+ list->npages++;
+ new = true;
+ goto again;
+}
+
+void heki_apply_permissions(struct heki_args *args)
+{
+ struct heki_hypervisor *hypervisor = heki.hypervisor;
+ struct heki_page_list *list = args->head;
+ phys_addr_t list_pa = args->head_pa;
+ struct page *page;
+ int ret;
+
+ if (!list)
+ return;
+
+ /* The very last one must be included. */
+ list->npages++;
+
+ /* Protect guest memory in the host page table. */
+ ret = hypervisor->protect_memory(list_pa);
+ if (ret) {
+ pr_warn("Failed to set memory permission\n");
+ return;
+ }
+
+ /* Free all the pages in the page list. */
+ while (list) {
+ page = pfn_to_page(list_pa >> PAGE_SHIFT);
+ list_pa = list->next_pa;
+ list = list->next;
+ __free_pages(page, 0);
+ }
}
static int __init heki_parse_config(char *str)
--
2.42.1
next prev parent reply other threads:[~2023-11-13 2:26 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-13 2:23 [RFC PATCH v2 00/19] Hypervisor-Enforced Kernel Integrity Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 01/19] virt: Introduce Hypervisor Enforced Kernel Integrity (Heki) Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 02/19] KVM: x86: Add new hypercall to lock control registers Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 03/19] KVM: x86: Add notifications for Heki policy configuration and violation Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 04/19] heki: Lock guest control registers at the end of guest kernel init Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 05/19] KVM: VMX: Add MBEC support Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 06/19] KVM: x86: Add kvm_x86_ops.fault_gva() Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 07/19] KVM: x86: Make memory attribute helpers more generic Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 08/19] KVM: x86: Extend kvm_vm_set_mem_attributes() with a mask Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 09/19] KVM: x86: Extend kvm_range_has_memory_attributes() with match_all Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 10/19] KVM: x86: Implement per-guest-page permissions Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 11/19] KVM: x86: Add new hypercall to set EPT permissions Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 12/19] x86: Implement the Memory Table feature to store arbitrary per-page data Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 13/19] heki: Implement a kernel page table walker Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 14/19] heki: x86: Initialize permissions counters for pages mapped into KVA Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 15/19] heki: x86: Initialize permissions counters for pages in vmap()/vunmap() Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 16/19] heki: x86: Update permissions counters when guest page permissions change Mickaël Salaün
2023-11-13 2:23 ` [RFC PATCH v2 17/19] heki: x86: Update permissions counters during text patching Mickaël Salaün
2023-11-13 8:19 ` Peter Zijlstra
2023-11-27 16:48 ` Madhavan T. Venkataraman
2023-11-27 20:08 ` Peter Zijlstra
2023-11-29 21:07 ` Madhavan T. Venkataraman
2023-11-30 11:33 ` Peter Zijlstra
2023-12-06 16:37 ` Madhavan T. Venkataraman
2023-12-06 18:51 ` Peter Zijlstra
2023-12-08 18:41 ` Madhavan T. Venkataraman
2023-12-01 0:45 ` Edgecombe, Rick P
2023-12-06 16:41 ` Madhavan T. Venkataraman
2023-11-13 2:23 ` Mickaël Salaün [this message]
2023-11-13 8:54 ` [RFC PATCH v2 18/19] heki: x86: Protect guest kernel memory using the KVM hypervisor Peter Zijlstra
2023-11-27 17:05 ` Madhavan T. Venkataraman
2023-11-27 20:03 ` Peter Zijlstra
2023-11-29 19:47 ` Madhavan T. Venkataraman
2023-11-13 2:23 ` [RFC PATCH v2 19/19] virt: Add Heki KUnit tests Mickaël Salaün
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231113022326.24388-19-mic@digikod.net \
--to=mic@digikod.net \
--cc=bp@alien8.de \
--cc=chao.p.peng@linux.intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=dev@lists.cloudhypervisor.org \
--cc=graf@amazon.com \
--cc=hpa@zytor.com \
--cc=jamorris@linux.microsoft.com \
--cc=jgowans@amazon.com \
--cc=john.s.andersen@intel.com \
--cc=keescook@chromium.org \
--cc=kvm@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=madvenka@linux.microsoft.com \
--cc=marian.c.rotariu@gmail.com \
--cc=mdontu@bitdefender.com \
--cc=mingo@redhat.com \
--cc=nicu.citu@icloud.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=quic_tsoni@quicinc.com \
--cc=rick.p.edgecombe@intel.com \
--cc=seanjc@google.com \
--cc=ssicleru@bitdefender.com \
--cc=tglx@linutronix.de \
--cc=tgopinath@microsoft.com \
--cc=virtualization@lists.linux-foundation.org \
--cc=vkuznets@redhat.com \
--cc=wanpengli@tencent.com \
--cc=wei.liu@kernel.org \
--cc=will@kernel.org \
--cc=x86@kernel.org \
--cc=xen-devel@lists.xenproject.org \
--cc=yu.c.zhang@linux.intel.com \
--cc=yuanyu@google.com \
--cc=ztarkhani@microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).