@@ -331,3 +331,34 @@ This command is always allowed.
};
Returns the number of online vCPUs.
+
+Events
+======
+
+The VM introspection events are sent using the KVMI_VM_EVENT message id.
+The message data begins with a common structure having the event id::
+
+ struct kvmi_event_hdr {
+ __u16 event;
+ __u16 padding[3];
+ };
+
+Specific event data can follow this common structure.
+
+1. KVMI_VM_EVENT_UNHOOK
+-----------------------
+
+:Architectures: all
+:Versions: >= 1
+:Actions: none
+:Parameters:
+
+::
+
+ struct kvmi_event_hdr;
+
+:Returns: none
+
+This event is sent when the device manager has to pause/stop/migrate the
+guest (see **Unhooking**). The introspection tool has a chance to unhook
+and close the KVMI channel (signaling that the operation can proceed).
@@ -14,7 +14,7 @@ kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \
$(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \
$(KVM)/dirty_ring.o
kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o
-kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o $(KVMI)/kvmi_msg.o
+kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o $(KVMI)/kvmi_msg.o kvmi.o
kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \
i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
new file mode 100644
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KVM introspection - x86
+ *
+ * Copyright (C) 2019-2020 Bitdefender S.R.L.
+ */
+
+void kvmi_arch_init_vcpu_events_mask(unsigned long *supported)
+{
+}
@@ -17,6 +17,8 @@ struct kvm_introspection {
unsigned long *cmd_allow_mask;
unsigned long *event_allow_mask;
+
+ atomic_t ev_seq;
};
int kvmi_version(void);
@@ -17,6 +17,8 @@ enum {
#define KVMI_VCPU_MESSAGE_ID(id) (((id) << 1) | 1)
enum {
+ KVMI_VM_EVENT = KVMI_VM_MESSAGE_ID(0),
+
KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1),
KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2),
KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3),
@@ -33,6 +35,8 @@ enum {
#define KVMI_VCPU_EVENT_ID(id) (((id) << 1) | 1)
enum {
+ KVMI_VM_EVENT_UNHOOK = KVMI_VM_EVENT_ID(0),
+
KVMI_NEXT_VM_EVENT
};
@@ -73,4 +77,9 @@ struct kvmi_vm_get_info_reply {
__u32 padding[3];
};
+struct kvmi_event_hdr {
+ __u16 event;
+ __u16 padding[3];
+};
+
#endif /* _UAPI__LINUX_KVMI_H */
@@ -68,6 +68,11 @@ static void set_event_perm(struct kvm_vm *vm, __s32 id, __u32 allow,
"KVM_INTROSPECTION_EVENT");
}
+static void disallow_event(struct kvm_vm *vm, __s32 event_id)
+{
+ set_event_perm(vm, event_id, 0, 0);
+}
+
static void allow_event(struct kvm_vm *vm, __s32 event_id)
{
set_event_perm(vm, event_id, 1, 0);
@@ -291,11 +296,16 @@ static void cmd_vm_check_event(__u16 id, int expected_err)
expected_err);
}
-static void test_cmd_vm_check_event(void)
+static void test_cmd_vm_check_event(struct kvm_vm *vm)
{
- __u16 invalid_id = 0xffff;
+ __u16 valid_id = KVMI_VM_EVENT_UNHOOK, invalid_id = 0xffff;
cmd_vm_check_event(invalid_id, -KVM_ENOENT);
+ cmd_vm_check_event(valid_id, 0);
+
+ disallow_event(vm, valid_id);
+ cmd_vm_check_event(valid_id, -KVM_EPERM);
+ allow_event(vm, valid_id);
}
static void test_cmd_vm_get_info(void)
@@ -312,6 +322,57 @@ static void test_cmd_vm_get_info(void)
pr_debug("vcpu count: %u\n", rpl.vcpu_count);
}
+static void trigger_event_unhook_notification(struct kvm_vm *vm)
+{
+ int r;
+
+ r = ioctl(vm->fd, KVM_INTROSPECTION_PREUNHOOK, NULL);
+ TEST_ASSERT(r == 0,
+ "KVM_INTROSPECTION_PREUNHOOK failed, errno %d (%s)\n",
+ errno, strerror(errno));
+}
+
+static void receive_event(struct kvmi_msg_hdr *msg_hdr, u16 msg_id,
+ struct kvmi_event_hdr *ev_hdr, u16 event_id,
+ size_t ev_size)
+{
+ size_t to_read = ev_size;
+
+ receive_data(msg_hdr, sizeof(*msg_hdr));
+
+ TEST_ASSERT(msg_hdr->id == msg_id,
+ "Unexpected messages id %d, expected %d\n",
+ msg_hdr->id, msg_id);
+
+ if (to_read > msg_hdr->size)
+ to_read = msg_hdr->size;
+
+ receive_data(ev_hdr, to_read);
+ TEST_ASSERT(ev_hdr->event == event_id,
+ "Unexpected event %d, expected %d\n",
+ ev_hdr->event, event_id);
+
+ TEST_ASSERT(msg_hdr->size == ev_size,
+ "Invalid event size %d, expected %zd bytes\n",
+ msg_hdr->size, ev_size);
+}
+
+static void receive_vm_event_unhook(void)
+{
+ struct kvmi_msg_hdr msg_hdr;
+ struct kvmi_event_hdr ev_hdr;
+
+ receive_event(&msg_hdr, KVMI_VM_EVENT,
+ &ev_hdr, KVMI_VM_EVENT_UNHOOK, sizeof(ev_hdr));
+}
+
+static void test_event_unhook(struct kvm_vm *vm)
+{
+ trigger_event_unhook_notification(vm);
+
+ receive_vm_event_unhook();
+}
+
static void test_introspection(struct kvm_vm *vm)
{
setup_socket();
@@ -320,8 +381,9 @@ static void test_introspection(struct kvm_vm *vm)
test_cmd_invalid();
test_cmd_get_version();
test_cmd_vm_check_command(vm);
- test_cmd_vm_check_event();
+ test_cmd_vm_check_event(vm);
test_cmd_vm_get_info();
+ test_event_unhook(vm);
unhook_introspection(vm);
}
@@ -17,6 +17,8 @@
static DECLARE_BITMAP(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS);
static DECLARE_BITMAP(Kvmi_known_events, KVMI_NUM_EVENTS);
+static DECLARE_BITMAP(Kvmi_known_vm_events, KVMI_NUM_EVENTS);
+static DECLARE_BITMAP(Kvmi_known_vcpu_events, KVMI_NUM_EVENTS);
static struct kmem_cache *msg_cache;
@@ -76,7 +78,14 @@ static void kvmi_init_always_allowed_commands(void)
static void kvmi_init_known_events(void)
{
- bitmap_zero(Kvmi_known_events, KVMI_NUM_EVENTS);
+ bitmap_zero(Kvmi_known_vm_events, KVMI_NUM_EVENTS);
+ set_bit(KVMI_VM_EVENT_UNHOOK, Kvmi_known_vm_events);
+
+ bitmap_zero(Kvmi_known_vcpu_events, KVMI_NUM_EVENTS);
+ kvmi_arch_init_vcpu_events_mask(Kvmi_known_vcpu_events);
+
+ bitmap_or(Kvmi_known_events, Kvmi_known_vm_events,
+ Kvmi_known_vcpu_events, KVMI_NUM_EVENTS);
}
int kvmi_init(void)
@@ -130,6 +139,8 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook)
bitmap_copy(kvmi->cmd_allow_mask, Kvmi_always_allowed_commands,
KVMI_NUM_COMMANDS);
+ atomic_set(&kvmi->ev_seq, 0);
+
kvmi->kvm = kvm;
return kvmi;
@@ -27,4 +27,7 @@ bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id);
bool kvmi_is_event_allowed(struct kvm_introspection *kvmi, u16 id);
bool kvmi_is_known_event(u16 id);
+/* arch */
+void kvmi_arch_init_vcpu_events_mask(unsigned long *supported);
+
#endif
@@ -186,7 +186,7 @@ static bool is_vm_message(u16 id)
static bool is_vm_command(u16 id)
{
- return is_vm_message(id);
+ return is_vm_message(id) && id != KVMI_VM_EVENT;
}
static struct kvmi_msg_hdr *kvmi_msg_recv(struct kvm_introspection *kvmi)
@@ -261,7 +261,45 @@ bool kvmi_msg_process(struct kvm_introspection *kvmi)
return err == 0;
}
+static void kvmi_fill_ev_msg_hdr(struct kvm_introspection *kvmi,
+ struct kvmi_msg_hdr *msg_hdr,
+ struct kvmi_event_hdr *ev_hdr,
+ u16 msg_id, u32 msg_seq,
+ size_t msg_size, u16 ev_id)
+{
+ memset(msg_hdr, 0, sizeof(*msg_hdr));
+ msg_hdr->id = msg_id;
+ msg_hdr->seq = msg_seq;
+ msg_hdr->size = msg_size - sizeof(*msg_hdr);
+
+ memset(ev_hdr, 0, sizeof(*ev_hdr));
+ ev_hdr->event = ev_id;
+}
+
+static void kvmi_fill_vm_event(struct kvm_introspection *kvmi,
+ struct kvmi_msg_hdr *msg_hdr,
+ struct kvmi_event_hdr *ev_hdr,
+ u16 ev_id, size_t msg_size)
+{
+ u32 msg_seq = atomic_inc_return(&kvmi->ev_seq);
+
+ kvmi_fill_ev_msg_hdr(kvmi, msg_hdr, ev_hdr, KVMI_VM_EVENT,
+ msg_seq, msg_size, ev_id);
+}
+
int kvmi_msg_send_unhook(struct kvm_introspection *kvmi)
{
- return -1;
+ struct kvmi_msg_hdr msg_hdr;
+ struct kvmi_event_hdr ev_hdr;
+ struct kvec vec[] = {
+ {.iov_base = &msg_hdr, .iov_len = sizeof(msg_hdr)},
+ {.iov_base = &ev_hdr, .iov_len = sizeof(ev_hdr) },
+ };
+ size_t msg_size = sizeof(msg_hdr) + sizeof(ev_hdr);
+ size_t n = ARRAY_SIZE(vec);
+
+ kvmi_fill_vm_event(kvmi, &msg_hdr, &ev_hdr,
+ KVMI_VM_EVENT_UNHOOK, msg_size);
+
+ return kvmi_sock_write(kvmi, vec, n, msg_size);
}
This event is sent when the guest is about to be paused/suspended/migrated. The introspection tool has the chance to remove its hooks (e.g. breakpoints) while the guest is still running. Signed-off-by: Adalbert Lazăr <alazar@bitdefender.com> --- Documentation/virt/kvm/kvmi.rst | 31 +++++++++ arch/x86/kvm/Makefile | 2 +- arch/x86/kvm/kvmi.c | 10 +++ include/linux/kvmi_host.h | 2 + include/uapi/linux/kvmi.h | 9 +++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 68 ++++++++++++++++++- virt/kvm/introspection/kvmi.c | 13 +++- virt/kvm/introspection/kvmi_int.h | 3 + virt/kvm/introspection/kvmi_msg.c | 42 +++++++++++- 9 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 arch/x86/kvm/kvmi.c