From: "Adalbert Lazăr" <alazar@bitdefender.com>
To: kvm@vger.kernel.org
Cc: linux-mm@kvack.org, virtualization@lists.linux-foundation.org,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Radim Krčmář" <rkrcmar@redhat.com>,
"Konrad Rzeszutek Wilk" <konrad.wilk@oracle.com>,
"Tamas K Lengyel" <tamas@tklengyel.com>,
"Mathieu Tarral" <mathieu.tarral@protonmail.com>,
"Samuel Laurén" <samuel.lauren@iki.fi>,
"Patrick Colp" <patrick.colp@oracle.com>,
"Jan Kiszka" <jan.kiszka@siemens.com>,
"Stefan Hajnoczi" <stefanha@redhat.com>,
"Weijiang Yang" <weijiang.yang@intel.com>,
Zhang@kvack.org, "Yu C" <yu.c.zhang@intel.com>,
"Mihai Donțu" <mdontu@bitdefender.com>,
"Adalbert Lazăr" <alazar@bitdefender.com>,
"Mircea Cîrjaliu" <mcirjaliu@bitdefender.com>
Subject: [RFC PATCH v6 72/92] kvm: introspection: add memory map/unmap support on the guest side
Date: Fri, 9 Aug 2019 19:00:27 +0300 [thread overview]
Message-ID: <20190809160047.8319-73-alazar@bitdefender.com> (raw)
In-Reply-To: <20190809160047.8319-1-alazar@bitdefender.com>
From: Mircea Cîrjaliu <mcirjaliu@bitdefender.com>
An introspection tool running in a dedicated VM can use the new device
(/dev/kvmmem) to map memory from other introspected VM-s.
Two ioctl operations are supported:
- KVM_HC_MEM_MAP/struct kvmi_mem_map
- KVM_HC_MEM_UNMAP/unsigned long
In order to map an introspected gpa to the local gva, the process using
this device needs to obtain a token from the host KVMI subsystem (see
Documentation/virtual/kvm/kvmi.rst - KVMI_GET_MAP_TOKEN).
Both operations use hypercalls (KVM_HC_MEM_MAP, KVM_HC_MEM_UNMAP)
to pass the requests to the host kernel/KVMi (see hypercalls.txt).
Signed-off-by: Mircea Cîrjaliu <mcirjaliu@bitdefender.com>
Signed-off-by: Adalbert Lazăr <alazar@bitdefender.com>
---
Documentation/virtual/kvm/hypercalls.txt | 34 ++
arch/x86/Kconfig | 9 +
arch/x86/include/asm/kvmi_guest.h | 10 +
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/kvmi_mem_guest.c | 26 +
include/uapi/linux/kvm_para.h | 2 +
include/uapi/linux/kvmi.h | 21 +
virt/kvm/kvmi_mem_guest.c | 651 +++++++++++++++++++++++
8 files changed, 754 insertions(+)
create mode 100644 arch/x86/include/asm/kvmi_guest.h
create mode 100644 arch/x86/kernel/kvmi_mem_guest.c
create mode 100644 virt/kvm/kvmi_mem_guest.c
diff --git a/Documentation/virtual/kvm/hypercalls.txt b/Documentation/virtual/kvm/hypercalls.txt
index 1ab59537b2fb..a47fae926201 100644
--- a/Documentation/virtual/kvm/hypercalls.txt
+++ b/Documentation/virtual/kvm/hypercalls.txt
@@ -173,3 +173,37 @@ The following registers are clobbered:
In particular, for KVM_HC_XEN_HVM_OP_GUEST_REQUEST_VM_EVENT, the last two
registers can be poisoned deliberately and cannot be used for passing
information.
+
+9. KVM_HC_MEM_MAP
+-----------------
+
+Architecture: x86
+Status: active
+Purpose: Map a guest physical page to another VM (the introspector).
+Usage:
+
+a0: pointer to a token obtained with a KVMI_GET_MAP_TOKEN command (see kvmi.rst)
+ struct kvmi_map_mem_token {
+ __u64 token[4];
+ };
+
+a1: guest physical address to be mapped
+
+a2: guest physical address from introspector that will be replaced
+
+Both guest physical addresses will end up poiting to the same physical page.
+
+Returns any error that the memory manager can return.
+
+10. KVM_HC_MEM_UNMAP
+-------------------
+
+Architecture: x86
+Status: active
+Purpose: Unmap a previously mapped page.
+Usage:
+
+a0: guest physical address from introspector
+
+The address will stop pointing to the introspected page and a new physical
+page is allocated for this gpa.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 68261430fe6e..a7527c1f90a0 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -820,6 +820,15 @@ config KVM_DEBUG_FS
Statistics are displayed in debugfs filesystem. Enabling this option
may incur significant overhead.
+config KVM_INTROSPECTION_GUEST
+ bool "KVM Memory Introspection support on Guest"
+ depends on KVM_GUEST
+ default n
+ help
+ This option enables functions and hypercalls for security applications
+ running in a separate VM to control the execution of other VM-s, query
+ the state of the vCPU-s (GPR-s, MSR-s etc.).
+
config PARAVIRT_TIME_ACCOUNTING
bool "Paravirtual steal time accounting"
depends on PARAVIRT
diff --git a/arch/x86/include/asm/kvmi_guest.h b/arch/x86/include/asm/kvmi_guest.h
new file mode 100644
index 000000000000..c7ed53a938e0
--- /dev/null
+++ b/arch/x86/include/asm/kvmi_guest.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __KVMI_GUEST_H__
+#define __KVMI_GUEST_H__
+
+long kvmi_arch_map_hc(struct kvmi_map_mem_token *tknp,
+ gpa_t req_gpa, gpa_t map_gpa);
+long kvmi_arch_unmap_hc(gpa_t map_gpa);
+
+
+#endif
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 00b7e27bc2b7..995652ba53b3 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -116,6 +116,7 @@ obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o
obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= paravirt-spinlocks.o
obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o
obj-$(CONFIG_X86_PMEM_LEGACY_DEVICE) += pmem.o
+obj-$(CONFIG_KVM_INTROSPECTION_GUEST) += kvmi_mem_guest.o ../../../virt/kvm/kvmi_mem_guest.o
obj-$(CONFIG_JAILHOUSE_GUEST) += jailhouse.o
diff --git a/arch/x86/kernel/kvmi_mem_guest.c b/arch/x86/kernel/kvmi_mem_guest.c
new file mode 100644
index 000000000000..c4e2613f90f3
--- /dev/null
+++ b/arch/x86/kernel/kvmi_mem_guest.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KVM introspection guest implementation
+ *
+ * Copyright (C) 2017 Bitdefender S.R.L.
+ *
+ * Author:
+ * Mircea Cirjaliu <mcirjaliu@bitdefender.com>
+ */
+
+#include <uapi/linux/kvmi.h>
+#include <uapi/linux/kvm_para.h>
+#include <linux/kvm_types.h>
+#include <asm/kvm_para.h>
+
+long kvmi_arch_map_hc(struct kvmi_map_mem_token *tknp,
+ gpa_t req_gpa, gpa_t map_gpa)
+{
+ return kvm_hypercall3(KVM_HC_MEM_MAP, (unsigned long)tknp,
+ req_gpa, map_gpa);
+}
+
+long kvmi_arch_unmap_hc(gpa_t map_gpa)
+{
+ return kvm_hypercall1(KVM_HC_MEM_UNMAP, map_gpa);
+}
diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h
index 592bda92b6d5..a083e3e66de6 100644
--- a/include/uapi/linux/kvm_para.h
+++ b/include/uapi/linux/kvm_para.h
@@ -33,6 +33,8 @@
#define KVM_HC_CLOCK_PAIRING 9
#define KVM_HC_SEND_IPI 10
+#define KVM_HC_MEM_MAP 32
+#define KVM_HC_MEM_UNMAP 33
#define KVM_HC_XEN_HVM_OP 34 /* Xen's __HYPERVISOR_hvm_op */
/*
diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h
index b072e0a4f33d..8591c748524f 100644
--- a/include/uapi/linux/kvmi.h
+++ b/include/uapi/linux/kvmi.h
@@ -262,4 +262,25 @@ struct kvmi_event_breakpoint {
__u8 padding[7];
};
+struct kvmi_map_mem_token {
+ __u64 token[4];
+};
+
+struct kvmi_get_map_token_reply {
+ struct kvmi_map_mem_token token;
+};
+
+/* Map other guest's gpa to local gva */
+struct kvmi_mem_map {
+ struct kvmi_map_mem_token token;
+ __u64 gpa;
+ __u64 gva;
+};
+
+/*
+ * ioctls for /dev/kvmmem
+ */
+#define KVM_INTRO_MEM_MAP _IOW('i', 0x01, struct kvmi_mem_map)
+#define KVM_INTRO_MEM_UNMAP _IOW('i', 0x02, unsigned long)
+
#endif /* _UAPI__LINUX_KVMI_H */
diff --git a/virt/kvm/kvmi_mem_guest.c b/virt/kvm/kvmi_mem_guest.c
new file mode 100644
index 000000000000..bec473b45289
--- /dev/null
+++ b/virt/kvm/kvmi_mem_guest.c
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KVM introspection guest implementation
+ *
+ * Copyright (C) 2017-2019 Bitdefender S.R.L.
+ *
+ * Author:
+ * Mircea Cirjaliu <mcirjaliu@bitdefender.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched/mm.h>
+#include <linux/types.h>
+#include <linux/kvm_types.h>
+#include <linux/kvm_para.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rwlock.h>
+#include <linux/hashtable.h>
+#include <linux/refcount.h>
+#include <linux/ioctl.h>
+
+#include <uapi/linux/kvmi.h>
+#include <asm/kvmi_guest.h>
+
+#define ASSERT(exp) BUG_ON(!(exp))
+#define DB_HASH_BITS 4
+
+static struct kmem_cache *proc_map_cachep;
+static struct kmem_cache *file_map_cachep;
+static struct kmem_cache *page_map_cachep;
+
+/* process/mm to proc_map */
+static DEFINE_HASHTABLE(db_hash, DB_HASH_BITS);
+static DEFINE_SPINLOCK(db_hash_lock);
+
+struct proc_map {
+ struct mm_struct *mm; /* database key */
+ struct hlist_node db_link; /* database link */
+ refcount_t refcnt;
+
+ struct rb_root entries; /* mapping entries for this mm */
+ rwlock_t entries_lock;
+};
+
+struct file_map {
+ struct proc_map *proc;
+
+ struct list_head entries; /* mapping entries for this file */
+ spinlock_t entries_lock;
+};
+
+struct page_map {
+ struct rb_node proc_link; /* link to struct proc_map */
+ struct list_head file_link; /* link to struct file_map */
+
+ gpa_t gpa; /* target GPA */
+ gva_t vaddr; /* local GVA */
+};
+
+static void proc_map_init(struct proc_map *pmap)
+{
+ pmap->mm = NULL;
+ INIT_HLIST_NODE(&pmap->db_link);
+ refcount_set(&pmap->refcnt, 0);
+
+ pmap->entries = RB_ROOT;
+ rwlock_init(&pmap->entries_lock);
+}
+
+static struct proc_map *proc_map_alloc(void)
+{
+ struct proc_map *obj;
+
+ obj = kmem_cache_alloc(proc_map_cachep, GFP_KERNEL);
+ if (obj != NULL)
+ proc_map_init(obj);
+
+ return obj;
+}
+
+static void proc_map_free(struct proc_map *pmap)
+{
+ ASSERT(hlist_unhashed(&pmap->db_link));
+ ASSERT(refcount_read(&pmap->refcnt) == 0);
+ ASSERT(RB_EMPTY_ROOT(&pmap->entries));
+
+ kmem_cache_free(proc_map_cachep, pmap);
+}
+
+static void file_map_init(struct file_map *fmp)
+{
+ INIT_LIST_HEAD(&fmp->entries);
+ spin_lock_init(&fmp->entries_lock);
+}
+
+static struct file_map *file_map_alloc(void)
+{
+ struct file_map *obj;
+
+ obj = kmem_cache_alloc(file_map_cachep, GFP_KERNEL);
+ if (obj != NULL)
+ file_map_init(obj);
+
+ return obj;
+}
+
+static void file_map_free(struct file_map *fmp)
+{
+ ASSERT(list_empty(&fmp->entries));
+
+ kmem_cache_free(file_map_cachep, fmp);
+}
+
+static void page_map_init(struct page_map *pmp)
+{
+ memset(pmp, 0, sizeof(*pmp));
+
+ RB_CLEAR_NODE(&pmp->proc_link);
+ INIT_LIST_HEAD(&pmp->file_link);
+}
+
+static struct page_map *page_map_alloc(void)
+{
+ struct page_map *obj;
+
+ obj = kmem_cache_alloc(page_map_cachep, GFP_KERNEL);
+ if (obj != NULL)
+ page_map_init(obj);
+
+ return obj;
+}
+
+static void page_map_free(struct page_map *pmp)
+{
+ ASSERT(RB_EMPTY_NODE(&pmp->proc_link));
+
+ kmem_cache_free(page_map_cachep, pmp);
+}
+
+static struct proc_map *get_proc_map(void)
+{
+ struct proc_map *pmap, *allocated;
+ struct mm_struct *mm;
+ bool found = false;
+
+ if (!mmget_not_zero(current->mm))
+ return NULL;
+ mm = current->mm;
+
+ allocated = proc_map_alloc(); /* may be NULL */
+
+ spin_lock(&db_hash_lock);
+
+ hash_for_each_possible(db_hash, pmap, db_link, (unsigned long)mm)
+ if (pmap->mm == mm && refcount_inc_not_zero(&pmap->refcnt)) {
+ found = true;
+ break;
+ }
+
+ if (!found && allocated != NULL) {
+ pmap = allocated;
+ allocated = NULL;
+
+ pmap->mm = mm;
+ hash_add(db_hash, &pmap->db_link, (unsigned long)mm);
+ refcount_set(&pmap->refcnt, 1);
+ } else
+ mmput(mm);
+
+ spin_unlock(&db_hash_lock);
+
+ if (allocated != NULL)
+ proc_map_free(allocated);
+
+ return pmap;
+}
+
+static void put_proc_map(struct proc_map *pmap)
+{
+ if (refcount_dec_and_test(&pmap->refcnt)) {
+ mmput(pmap->mm);
+
+ /* remove from hash table */
+ spin_lock(&db_hash_lock);
+ hash_del(&pmap->db_link);
+ spin_unlock(&db_hash_lock);
+
+ proc_map_free(pmap);
+ }
+}
+
+static bool proc_map_insert(struct proc_map *pmap, struct page_map *pmp)
+{
+ struct rb_root *root = &pmap->entries;
+ struct rb_node **new = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct page_map *this;
+ bool inserted = true;
+
+ write_lock(&pmap->entries_lock);
+
+ /* Figure out where to put new node */
+ while (*new) {
+ this = rb_entry(*new, struct page_map, proc_link);
+
+ parent = *new;
+ if (pmp->vaddr < this->vaddr)
+ new = &((*new)->rb_left);
+ else if (pmp->vaddr > this->vaddr)
+ new = &((*new)->rb_right);
+ else {
+ /* Already have this address */
+ inserted = false;
+ goto out;
+ }
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&pmp->proc_link, parent, new);
+ rb_insert_color(&pmp->proc_link, root);
+
+out:
+ write_unlock(&pmap->entries_lock);
+
+ return inserted;
+}
+
+#if 0 /* will use this later */
+static struct page_map *proc_map_search(struct proc_map *pmap,
+ unsigned long vaddr)
+{
+ struct rb_root *root = &pmap->entries;
+ struct rb_node *node;
+ struct page_map *pmp;
+
+ read_lock(&pmap->entries_lock);
+
+ node = root->rb_node;
+
+ while (node) {
+ pmp = rb_entry(node, struct page_map, proc_link);
+
+ if (vaddr < pmp->vaddr)
+ node = node->rb_left;
+ else if (vaddr > pmp->vaddr)
+ node = node->rb_right;
+ else
+ break;
+ }
+
+ if (!node)
+ pmp = NULL;
+
+ read_unlock(&pmap->entries_lock);
+
+ return pmp;
+}
+#endif
+
+static struct page_map *proc_map_search_extract(struct proc_map *pmap,
+ unsigned long vaddr)
+{
+ struct rb_root *root = &pmap->entries;
+ struct rb_node *node;
+ struct page_map *pmp;
+
+ write_lock(&pmap->entries_lock);
+
+ node = root->rb_node;
+
+ while (node) {
+ pmp = rb_entry(node, struct page_map, proc_link);
+
+ if (vaddr < pmp->vaddr)
+ node = node->rb_left;
+ else if (vaddr > pmp->vaddr)
+ node = node->rb_right;
+ else
+ break;
+ }
+
+ if (node) {
+ rb_erase(&pmp->proc_link, &pmap->entries);
+ RB_CLEAR_NODE(&pmp->proc_link);
+ } else
+ pmp = NULL;
+
+ write_unlock(&pmap->entries_lock);
+
+ return pmp;
+}
+
+static void proc_map_remove(struct proc_map *pmap, struct page_map *pmp)
+{
+ write_lock(&pmap->entries_lock);
+ rb_erase(&pmp->proc_link, &pmap->entries);
+ RB_CLEAR_NODE(&pmp->proc_link);
+ write_unlock(&pmap->entries_lock);
+}
+
+static void file_map_insert(struct file_map *fmp, struct page_map *pmp)
+{
+ spin_lock(&fmp->entries_lock);
+ list_add(&pmp->file_link, &fmp->entries);
+ spin_unlock(&fmp->entries_lock);
+}
+
+static void file_map_remove(struct file_map *fmp, struct page_map *pmp)
+{
+ spin_lock(&fmp->entries_lock);
+ list_del(&pmp->file_link);
+ spin_unlock(&fmp->entries_lock);
+}
+
+/*
+ * Opens the device for map/unmap operations. The mm of this process is
+ * associated with these files in a 1:many relationship.
+ * Operations on this file must be done within the same process that opened it.
+ */
+static int kvm_dev_open(struct inode *inodep, struct file *filp)
+{
+ struct proc_map *pmap;
+ struct file_map *fmp;
+
+ pr_debug("kvmi: file %016lx opened by mm %016lx\n",
+ (unsigned long) filp, (unsigned long)current->mm);
+
+ pmap = get_proc_map();
+ if (pmap == NULL)
+ return -ENOENT;
+
+ /* link the file 1:1 with such a structure */
+ fmp = file_map_alloc();
+ if (fmp == NULL)
+ return -ENOMEM;
+
+ fmp->proc = pmap;
+ filp->private_data = fmp;
+
+ return 0;
+}
+
+static long _do_mapping(struct kvmi_mem_map *map_req, struct page_map *pmp)
+{
+ struct page *page;
+ phys_addr_t paddr;
+ long nrpages;
+ long result = 0;
+
+ down_read(¤t->mm->mmap_sem);
+
+ /* pin the page to be replaced (also swaps in the page) */
+ nrpages = get_user_pages_locked(map_req->gva, 1,
+ FOLL_SPLIT | FOLL_MIGRATION,
+ &page, NULL);
+ if (unlikely(nrpages == 0)) {
+ result = -ENOENT;
+ pr_err("kvmi: found no page for %016llx\n", map_req->gva);
+ goto out;
+ } else if (IS_ERR_VALUE(nrpages)) {
+ result = nrpages;
+ pr_err("kvmi: get_user_pages_locked() failed (%ld)\n", result);
+ goto out;
+ }
+
+ paddr = page_to_phys(page);
+ pr_debug("%s: page phys addr %016llx\n", __func__, paddr);
+
+ /* last thing to do is host mapping */
+ result = kvmi_arch_map_hc(&map_req->token, map_req->gpa, paddr);
+ if (IS_ERR_VALUE(result)) {
+ pr_err("kvmi: mapping failed for %016llx -> %016lx (%ld)\n",
+ pmp->gpa, pmp->vaddr, result);
+
+ /* don't need this page anymore */
+ put_page(page);
+ }
+
+out:
+ up_read(¤t->mm->mmap_sem);
+
+ return result;
+}
+
+static long _do_unmapping(struct mm_struct *mm, struct page_map *pmp)
+{
+ struct vm_area_struct *vma;
+ struct page *page;
+ phys_addr_t paddr;
+ long result = 0;
+
+ down_read(&mm->mmap_sem);
+
+ /* find the VMA for the virtual address */
+ vma = find_vma(mm, pmp->vaddr);
+ if (vma == NULL) {
+ result = -ENOENT;
+ pr_err("kvmi: find_vma() found no VMA\n");
+ goto out;
+ }
+
+ /* the page is pinned, thus easy to access */
+ page = follow_page(vma, pmp->vaddr, 0);
+ if (IS_ERR_VALUE(page)) {
+ result = PTR_ERR(page);
+ pr_err("kvmi: follow_page() failed (%ld)\n", result);
+ goto out;
+ } else if (page == NULL) {
+ result = -ENOENT;
+ pr_err("kvmi: follow_page() found no page\n");
+ goto out;
+ }
+
+ paddr = page_to_phys(page);
+ pr_debug("%s: page phys addr %016llx\n", __func__, paddr);
+
+ /* last thing to do is host unmapping */
+ result = kvmi_arch_unmap_hc(paddr);
+ if (IS_ERR_VALUE(result))
+ pr_warn("kvmi: unmapping failed for %016lx (%ld)\n",
+ pmp->vaddr, result);
+
+ /* finally unpin the page */
+ put_page(page);
+
+out:
+ up_read(&mm->mmap_sem);
+
+ return result;
+}
+
+static noinline long kvm_dev_ioctl_map(struct file_map *fmp,
+ struct kvmi_mem_map *map)
+{
+ struct proc_map *pmap = fmp->proc;
+ struct page_map *pmp;
+ bool added;
+ long result = 0;
+
+ pr_debug("kvmi: mm %016lx map request %016llx -> %016llx\n",
+ (unsigned long)current->mm, map->gpa, map->gva);
+
+ if (!access_ok(map->gva, PAGE_SIZE))
+ return -EINVAL;
+
+ /* prepare list entry */
+ pmp = page_map_alloc();
+ if (pmp == NULL)
+ return -ENOMEM;
+
+ pmp->gpa = map->gpa;
+ pmp->vaddr = map->gva;
+
+ added = proc_map_insert(pmap, pmp);
+ if (added == false) {
+ result = -EALREADY;
+ pr_err("kvmi: address %016llx already mapped into\n", map->gva);
+ goto out_free;
+ }
+ file_map_insert(fmp, pmp);
+
+ /* actual mapping here */
+ result = _do_mapping(map, pmp);
+ if (IS_ERR_VALUE(result))
+ goto out_remove;
+
+ return 0;
+
+out_remove:
+ proc_map_remove(pmap, pmp);
+ file_map_remove(fmp, pmp);
+
+out_free:
+ page_map_free(pmp);
+
+ return result;
+}
+
+static noinline long kvm_dev_ioctl_unmap(struct file_map *fmp,
+ unsigned long vaddr)
+{
+ struct proc_map *pmap = fmp->proc;
+ struct page_map *pmp;
+ long result = 0;
+
+ pr_debug("kvmi: mm %016lx unmap request %016lx\n",
+ (unsigned long)current->mm, vaddr);
+
+ pmp = proc_map_search_extract(pmap, vaddr);
+ if (pmp == NULL) {
+ pr_err("kvmi: address %016lx not mapped\n", vaddr);
+ return -ENOENT;
+ }
+
+ /* actual unmapping here */
+ result = _do_unmapping(current->mm, pmp);
+
+ file_map_remove(fmp, pmp);
+ page_map_free(pmp);
+
+ return result;
+}
+
+/*
+ * Operations on this file must be done within the same process that opened it.
+ */
+static long kvm_dev_ioctl(struct file *filp,
+ unsigned int ioctl, unsigned long arg)
+{
+ void __user *argp = (void __user *) arg;
+ struct file_map *fmp = filp->private_data;
+ struct proc_map *pmap = fmp->proc;
+ long result;
+
+ /* this helps keep my code simpler */
+ if (current->mm != pmap->mm) {
+ pr_err("kvmi: ioctl request by different process\n");
+ return -EINVAL;
+ }
+
+ switch (ioctl) {
+ case KVM_INTRO_MEM_MAP: {
+ struct kvmi_mem_map map;
+
+ result = -EFAULT;
+ if (copy_from_user(&map, argp, sizeof(map)))
+ break;
+
+ result = kvm_dev_ioctl_map(fmp, &map);
+ break;
+ }
+ case KVM_INTRO_MEM_UNMAP: {
+ unsigned long vaddr = (unsigned long) arg;
+
+ result = kvm_dev_ioctl_unmap(fmp, vaddr);
+ break;
+ }
+ default:
+ pr_err("kvmi: ioctl %d not implemented\n", ioctl);
+ result = -ENOTTY;
+ }
+
+ return result;
+}
+
+/*
+ * No constraint on closing the device.
+ */
+static int kvm_dev_release(struct inode *inodep, struct file *filp)
+{
+ struct file_map *fmp = filp->private_data;
+ struct proc_map *pmap = fmp->proc;
+ struct page_map *pmp, *temp;
+
+ pr_debug("kvmi: file %016lx closed by mm %016lx\n",
+ (unsigned long) filp, (unsigned long)current->mm);
+
+ /* this file_map has no more users, thus no more concurrent access */
+ list_for_each_entry_safe(pmp, temp, &fmp->entries, file_link) {
+ proc_map_remove(pmap, pmp);
+ list_del(&pmp->file_link);
+
+ _do_unmapping(pmap->mm, pmp);
+
+ page_map_free(pmp);
+ }
+
+ file_map_free(fmp);
+ put_proc_map(pmap);
+
+ return 0;
+}
+
+static const struct file_operations kvmmem_ops = {
+ .open = kvm_dev_open,
+ .unlocked_ioctl = kvm_dev_ioctl,
+ .compat_ioctl = kvm_dev_ioctl,
+ .release = kvm_dev_release,
+};
+
+static struct miscdevice kvm_mem_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "kvmmem",
+ .fops = &kvmmem_ops,
+};
+
+static int __init kvm_intro_guest_init(void)
+{
+ int result = 0;
+
+ if (!kvm_para_available()) {
+ pr_err("kvmi: paravirt not available\n");
+ return -EPERM;
+ }
+
+ proc_map_cachep = KMEM_CACHE(proc_map, SLAB_PANIC | SLAB_ACCOUNT);
+ if (proc_map_cachep == NULL) {
+ result = -ENOMEM;
+ goto out_err;
+ }
+
+ file_map_cachep = KMEM_CACHE(file_map, SLAB_PANIC | SLAB_ACCOUNT);
+ if (file_map_cachep == NULL) {
+ result = -ENOMEM;
+ goto out_err;
+ }
+
+ page_map_cachep = KMEM_CACHE(page_map, SLAB_PANIC | SLAB_ACCOUNT);
+ if (page_map_cachep == NULL) {
+ result = -ENOMEM;
+ goto out_err;
+ }
+
+ result = misc_register(&kvm_mem_dev);
+ if (result) {
+ pr_err("kvmi: misc device register failed (%d)\n", result);
+ goto out_err;
+ }
+
+ pr_debug("kvmi: guest memory introspection device created\n");
+
+ return 0;
+
+out_err:
+ kmem_cache_destroy(page_map_cachep);
+ kmem_cache_destroy(file_map_cachep);
+ kmem_cache_destroy(proc_map_cachep);
+
+ return result;
+}
+
+static void __exit kvm_intro_guest_exit(void)
+{
+ misc_deregister(&kvm_mem_dev);
+
+ kmem_cache_destroy(page_map_cachep);
+ kmem_cache_destroy(file_map_cachep);
+ kmem_cache_destroy(proc_map_cachep);
+}
+
+module_init(kvm_intro_guest_init)
+module_exit(kvm_intro_guest_exit)
next prev parent reply other threads:[~2019-08-09 16:05 UTC|newest]
Thread overview: 168+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-08-09 15:59 [RFC PATCH v6 00/92] VM introspection Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 01/92] kvm: introduce KVMI (VM introspection subsystem) Adalbert Lazăr
2019-08-12 20:20 ` Sean Christopherson
2019-08-13 9:11 ` Paolo Bonzini
2019-08-13 11:57 ` Adalbert Lazăr
[not found] ` <5d52a5ae.1c69fb81.5c260.1573SMTPIN_ADDED_BROKEN@mx.google.com>
2019-08-13 12:09 ` Paolo Bonzini
2019-08-13 15:01 ` Sean Christopherson
2019-08-13 21:03 ` Paolo Bonzini
2019-08-14 9:48 ` Adalbert Lazăr
[not found] ` <5d53d8d1.1c69fb81.7d32.0bedSMTPIN_ADDED_BROKEN@mx.google.com>
2019-08-14 10:37 ` Paolo Bonzini
2019-08-09 15:59 ` [RFC PATCH v6 02/92] kvm: introspection: add basic ioctls (hook/unhook) Adalbert Lazăr
2019-08-13 8:44 ` Paolo Bonzini
2019-08-13 14:24 ` Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 03/92] kvm: introspection: add permission access ioctls Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 04/92] kvm: introspection: add the read/dispatch message function Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 05/92] kvm: introspection: add KVMI_GET_VERSION Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 06/92] kvm: introspection: add KVMI_CONTROL_CMD_RESPONSE Adalbert Lazăr
2019-08-13 9:15 ` Paolo Bonzini
2019-08-13 17:08 ` Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 07/92] kvm: introspection: honor the reply option when handling the KVMI_GET_VERSION command Adalbert Lazăr
2019-08-13 9:16 ` Paolo Bonzini
2019-08-09 15:59 ` [RFC PATCH v6 08/92] kvm: introspection: add KVMI_CHECK_COMMAND and KVMI_CHECK_EVENT Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 09/92] kvm: introspection: add KVMI_GET_GUEST_INFO Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 10/92] kvm: introspection: add KVMI_CONTROL_VM_EVENTS Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 11/92] kvm: introspection: add vCPU related data Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 12/92] kvm: introspection: add a jobs list to every introspected vCPU Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 13/92] kvm: introspection: make the vCPU wait even when its jobs list is empty Adalbert Lazăr
2019-08-13 8:43 ` Paolo Bonzini
2019-08-13 14:19 ` Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 14/92] kvm: introspection: handle introspection commands before returning to guest Adalbert Lazăr
2019-08-13 8:26 ` Paolo Bonzini
2019-08-13 13:54 ` Adalbert Lazăr
[not found] ` <5d52c10e.1c69fb81.26904.fd34SMTPIN_ADDED_BROKEN@mx.google.com>
2019-08-13 14:45 ` Paolo Bonzini
2019-08-14 9:39 ` Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 15/92] kvm: introspection: handle vCPU related introspection commands Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 16/92] kvm: introspection: handle events and event replies Adalbert Lazăr
2019-08-13 8:55 ` Paolo Bonzini
2019-08-13 15:25 ` Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 17/92] kvm: introspection: introduce event actions Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 18/92] kvm: introspection: add KVMI_EVENT_UNHOOK Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 19/92] kvm: introspection: add KVMI_EVENT_CREATE_VCPU Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 20/92] kvm: introspection: add KVMI_GET_VCPU_INFO Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 21/92] kvm: page track: add track_create_slot() callback Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 22/92] kvm: x86: provide all page tracking hooks with the guest virtual address Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 23/92] kvm: page track: add support for preread, prewrite and preexec Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 24/92] kvm: x86: wire in the preread/prewrite/preexec page trackers Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 25/92] kvm: x86: intercept the write access on sidt and other emulated instructions Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 26/92] kvm: x86: add kvm_mmu_nested_pagefault() Adalbert Lazăr
2019-08-13 8:12 ` Paolo Bonzini
2019-08-09 15:59 ` [RFC PATCH v6 27/92] kvm: introspection: use page track Adalbert Lazăr
2019-08-13 9:06 ` Paolo Bonzini
2019-08-09 15:59 ` [RFC PATCH v6 28/92] kvm: x86: consult the page tracking from kvm_mmu_get_page() and __direct_map() Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 29/92] kvm: introspection: add KVMI_CONTROL_EVENTS Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 30/92] kvm: x86: add kvm_spt_fault() Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 31/92] kvm: introspection: add KVMI_EVENT_PF Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 32/92] kvm: introspection: add KVMI_GET_PAGE_ACCESS Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 33/92] kvm: introspection: add KVMI_SET_PAGE_ACCESS Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 34/92] Documentation: Introduce EPT based Subpage Protection Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 35/92] KVM: VMX: Add control flags for SPP enabling Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 36/92] KVM: VMX: Implement functions for SPPT paging setup Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 37/92] KVM: VMX: Introduce SPP access bitmap and operation functions Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 38/92] KVM: VMX: Add init/set/get functions for SPP Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 39/92] KVM: VMX: Introduce SPP user-space IOCTLs Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 40/92] KVM: VMX: Handle SPP induced vmexit and page fault Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 41/92] KVM: MMU: Enable Lazy mode SPPT setup Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 42/92] KVM: MMU: Handle host memory remapping and reclaim Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 43/92] kvm: introspection: add KVMI_CONTROL_SPP Adalbert Lazăr
2019-08-09 15:59 ` [RFC PATCH v6 44/92] kvm: introspection: extend the internal database of tracked pages with write_bitmap info Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 45/92] kvm: introspection: add KVMI_GET_PAGE_WRITE_BITMAP Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 46/92] kvm: introspection: add KVMI_SET_PAGE_WRITE_BITMAP Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 47/92] kvm: introspection: add KVMI_READ_PHYSICAL and KVMI_WRITE_PHYSICAL Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 48/92] kvm: add kvm_vcpu_kick_and_wait() Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 49/92] kvm: introspection: add KVMI_PAUSE_VCPU and KVMI_EVENT_PAUSE_VCPU Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 50/92] kvm: introspection: add KVMI_GET_REGISTERS Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 51/92] kvm: introspection: add KVMI_SET_REGISTERS Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 52/92] kvm: introspection: add KVMI_GET_CPUID Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 53/92] kvm: introspection: add KVMI_INJECT_EXCEPTION + KVMI_EVENT_TRAP Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 54/92] kvm: introspection: add KVMI_CONTROL_CR and KVMI_EVENT_CR Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 55/92] kvm: introspection: add KVMI_CONTROL_MSR and KVMI_EVENT_MSR Adalbert Lazăr
2019-08-12 21:05 ` Sean Christopherson
2019-08-15 6:36 ` Nicusor CITU
2019-08-19 18:36 ` Sean Christopherson
2019-08-20 8:44 ` Nicusor CITU
2019-08-20 11:43 ` Mihai Donțu
2019-08-21 15:18 ` Sean Christopherson
2019-08-19 18:52 ` Sean Christopherson
2019-08-09 16:00 ` [RFC PATCH v6 56/92] kvm: x86: block any attempt to disable MSR interception if tracked by introspection Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 57/92] kvm: introspection: add KVMI_GET_XSAVE Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 58/92] kvm: introspection: add KVMI_GET_MTRR_TYPE Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 59/92] kvm: introspection: add KVMI_EVENT_XSETBV Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 60/92] kvm: x86: add kvm_arch_vcpu_set_guest_debug() Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 61/92] kvm: introspection: add KVMI_EVENT_BREAKPOINT Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 62/92] kvm: introspection: add KVMI_EVENT_HYPERCALL Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 63/92] kvm: introspection: add KVMI_EVENT_DESCRIPTOR Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 64/92] kvm: introspection: add single-stepping Adalbert Lazăr
2019-08-12 20:50 ` Sean Christopherson
2019-08-13 12:51 ` Adalbert Lazăr
2019-08-14 12:36 ` Nicusor CITU
2019-08-14 12:53 ` Paolo Bonzini
2019-08-09 16:00 ` [RFC PATCH v6 65/92] kvm: introspection: add KVMI_EVENT_SINGLESTEP Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 66/92] kvm: introspection: add custom input when single-stepping a vCPU Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 67/92] kvm: introspection: use single stepping on unimplemented instructions Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 68/92] kvm: x86: emulate a guest page table walk on SPT violations due to A/D bit updates Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 69/92] kvm: x86: keep the page protected if tracked by the introspection tool Adalbert Lazăr
2019-09-10 14:26 ` Konrad Rzeszutek Wilk
2019-09-10 16:28 ` Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 70/92] kvm: x86: filter out access rights only when " Adalbert Lazăr
2019-08-13 9:08 ` Paolo Bonzini
2019-08-13 16:06 ` Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 71/92] mm: add support for remote mapping Adalbert Lazăr
2019-08-09 16:24 ` DANGER WILL ROBINSON, DANGER Matthew Wilcox
2019-08-13 9:29 ` Paolo Bonzini
2019-08-13 11:24 ` Matthew Wilcox
2019-08-13 12:02 ` Paolo Bonzini
2019-08-13 11:01 ` Adalbert Lazăr
2019-08-15 19:19 ` Jerome Glisse
2019-08-15 20:16 ` Jerome Glisse
2019-08-16 17:45 ` Jason Gunthorpe
2019-08-23 12:39 ` Mircea CIRJALIU - MELIU
2019-09-05 18:09 ` Jerome Glisse
2019-09-09 17:00 ` Paolo Bonzini
2019-09-10 7:49 ` Mircea CIRJALIU - MELIU
2019-10-02 19:27 ` Jerome Glisse
2019-10-02 13:46 ` Paolo Bonzini
2019-10-02 14:15 ` Jerome Glisse
2019-10-02 16:18 ` Paolo Bonzini
2019-10-02 17:04 ` Jerome Glisse
2019-10-02 20:10 ` Paolo Bonzini
2019-10-03 15:42 ` Jerome Glisse
2019-10-03 15:50 ` Paolo Bonzini
2019-10-03 16:42 ` Mircea CIRJALIU - MELIU
2019-10-03 18:31 ` Jerome Glisse
2019-10-03 19:38 ` Paolo Bonzini
2019-10-04 9:41 ` Mircea CIRJALIU - MELIU
2019-10-04 11:46 ` Paolo Bonzini
2019-10-03 16:36 ` Mircea CIRJALIU - MELIU
2019-08-09 16:00 ` Adalbert Lazăr [this message]
2019-08-09 16:00 ` [RFC PATCH v6 73/92] kvm: introspection: use remote mapping Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 74/92] kvm: x86: do not unconditionally patch the hypercall instruction during emulation Adalbert Lazăr
2019-08-13 9:20 ` Paolo Bonzini
2019-08-14 12:07 ` Adalbert Lazăr
[not found] ` <5d53f965.1c69fb81.cd952.035bSMTPIN_ADDED_BROKEN@mx.google.com>
2019-08-14 12:33 ` Paolo Bonzini
2019-08-09 16:00 ` [RFC PATCH v6 75/92] kvm: x86: disable gpa_available optimization in emulator_read_write_onepage() Adalbert Lazăr
2019-08-13 8:47 ` Paolo Bonzini
2019-08-13 14:33 ` Adalbert Lazăr
[not found] ` <5d52ca22.1c69fb81.4ceb8.e90bSMTPIN_ADDED_BROKEN@mx.google.com>
2019-08-13 14:35 ` Paolo Bonzini
2019-08-09 16:00 ` [RFC PATCH v6 76/92] kvm: x86: disable EPT A/D bits if introspection is present Adalbert Lazăr
2019-08-13 9:18 ` Paolo Bonzini
2019-08-09 16:00 ` [RFC PATCH v6 77/92] kvm: introspection: add trace functions Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 78/92] kvm: x86: add tracepoints for interrupt and exception injections Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 79/92] kvm: x86: emulate movsd xmm, m64 Adalbert Lazăr
2019-08-13 9:17 ` Paolo Bonzini
2019-08-09 16:00 ` [RFC PATCH v6 80/92] kvm: x86: emulate movss xmm, m32 Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 81/92] kvm: x86: emulate movq xmm, m64 Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 82/92] kvm: x86: emulate movq r, xmm Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 83/92] kvm: x86: emulate movd xmm, m32 Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 84/92] kvm: x86: enable the half part of movss, movsd, movups Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 85/92] kvm: x86: emulate lfence Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 86/92] kvm: x86: emulate xorpd xmm2/m128, xmm1 Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 87/92] kvm: x86: emulate xorps xmm/m128, xmm Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 88/92] kvm: x86: emulate fst/fstp m64fp Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 89/92] kvm: x86: make lock cmpxchg r, r/m atomic Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 90/92] kvm: x86: emulate lock cmpxchg8b atomically Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 91/92] kvm: x86: emulate lock cmpxchg16b m128 Adalbert Lazăr
2019-08-09 16:00 ` [RFC PATCH v6 92/92] kvm: x86: fallback to the single-step on multipage CMPXCHG emulation Adalbert Lazăr
2019-08-12 18:23 ` [RFC PATCH v6 00/92] VM introspection Sean Christopherson
2019-08-12 21:40 ` Sean Christopherson
2019-08-13 9:34 ` Paolo Bonzini
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=20190809160047.8319-73-alazar@bitdefender.com \
--to=alazar@bitdefender.com \
--cc=Zhang@kvack.org \
--cc=jan.kiszka@siemens.com \
--cc=konrad.wilk@oracle.com \
--cc=kvm@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mathieu.tarral@protonmail.com \
--cc=mcirjaliu@bitdefender.com \
--cc=mdontu@bitdefender.com \
--cc=patrick.colp@oracle.com \
--cc=pbonzini@redhat.com \
--cc=rkrcmar@redhat.com \
--cc=samuel.lauren@iki.fi \
--cc=stefanha@redhat.com \
--cc=tamas@tklengyel.com \
--cc=virtualization@lists.linux-foundation.org \
--cc=weijiang.yang@intel.com \
--cc=yu.c.zhang@intel.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).