KVM_INTROSPECTION_COMMAND and KVM_INTROSPECTION_EVENTS should be used
by userspace/QEMU to allow access to specific (or all) introspection
commands and events.

By default, all introspection events and almost all introspection commands
are disallowed. There are a couple of commands that are always allowed
(those querying the introspection capabilities).

Signed-off-by: Adalbert Lazăr <ala...@bitdefender.com>
---
 Documentation/virtual/kvm/api.txt | 56 +++++++++++++++++++-
 include/uapi/linux/kvm.h          |  6 +++
 virt/kvm/kvm_main.c               |  6 +++
 virt/kvm/kvmi.c                   | 85 +++++++++++++++++++++++++++++++
 virt/kvm/kvmi_int.h               | 51 +++++++++++++++++++
 5 files changed, 203 insertions(+), 1 deletion(-)

diff --git a/Documentation/virtual/kvm/api.txt 
b/Documentation/virtual/kvm/api.txt
index 28d4429f9ae9..ea3135d365c7 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -3889,7 +3889,61 @@ It will fail with -EINVAL if padding is not zero.
 The KVMI version can be retrieved using the KVM_CAP_INTROSPECTION of
 the KVM_CHECK_EXTENSION ioctl() at run-time.
 
-4.997 KVM_INTROSPECTION_UNHOOK
+4.997 KVM_INTROSPECTION_COMMAND
+
+Capability: KVM_CAP_INTROSPECTION
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_introspection_feature (in)
+Returns: 0 on success, a negative value on error
+
+This ioctl is used to allow or disallow introspection commands
+for the current VM. By default, almost all commands are disallowed
+except for those used to query the API.
+
+struct kvm_introspection_feature {
+       __u32 allow;
+       __s32 id;
+};
+
+If allow is 1, the command specified by id is allowed. If allow is 0,
+the command is disallowed.
+
+Unless set to -1 (meaning all commands), id must be a command ID
+(e.g. KVMI_GET_VERSION, KVMI_GET_GUEST_INFO etc.)
+
+Errors:
+
+  -EINVAL if the command is unknown
+  -EPERM if the command can't be disallowed (e.g. KVMI_GET_VERSION)
+
+4.998 KVM_INTROSPECTION_EVENT
+
+Capability: KVM_CAP_INTROSPECTION
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_introspection_feature (in)
+Returns: 0 on success, a negative value on error
+
+This ioctl is used to allow or disallow introspection events
+for the current VM. By default, all events are disallowed.
+
+struct kvm_introspection_feature {
+       __u32 allow;
+       __s32 id;
+};
+
+If allow is 1, the event specified by id is allowed. If allow is 0,
+the event is disallowed.
+
+Unless set to -1 (meaning all event), id must be a event ID
+(e.g. KVMI_EVENT_UNHOOK, KVMI_EVENT_CR, etc.)
+
+Errors:
+
+  -EINVAL if the event is unknown
+
+4.999 KVM_INTROSPECTION_UNHOOK
 
 Capability: KVM_CAP_INTROSPECTION
 Architectures: x86
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index bae37bf37338..2ff05fd123e3 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1527,9 +1527,15 @@ struct kvm_introspection {
        __u32 padding;
        __u8 uuid[16];
 };
+struct kvm_introspection_feature {
+       __u32 allow;
+       __s32 id;
+};
 #define KVM_INTROSPECTION_HOOK    _IOW(KVMIO, 0xff, struct kvm_introspection)
 #define KVM_INTROSPECTION_UNHOOK  _IO(KVMIO, 0xfe)
 /* write true on force-reset, false otherwise */
+#define KVM_INTROSPECTION_COMMAND _IOW(KVMIO, 0xfd, struct 
kvm_introspection_feature)
+#define KVM_INTROSPECTION_EVENT   _IOW(KVMIO, 0xfc, struct 
kvm_introspection_feature)
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU    (1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3         (1 << 1)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 09a930ac007d..8399b826f2d2 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3270,6 +3270,12 @@ static long kvm_vm_ioctl(struct file *filp,
        case KVM_INTROSPECTION_HOOK:
                r = kvmi_ioctl_hook(kvm, argp);
                break;
+       case KVM_INTROSPECTION_COMMAND:
+               r = kvmi_ioctl_command(kvm, argp);
+               break;
+       case KVM_INTROSPECTION_EVENT:
+               r = kvmi_ioctl_event(kvm, argp);
+               break;
        case KVM_INTROSPECTION_UNHOOK:
                r = kvmi_ioctl_unhook(kvm, arg);
                break;
diff --git a/virt/kvm/kvmi.c b/virt/kvm/kvmi.c
index 591f6ee22135..dc64f975998f 100644
--- a/virt/kvm/kvmi.c
+++ b/virt/kvm/kvmi.c
@@ -169,6 +169,91 @@ int kvmi_ioctl_hook(struct kvm *kvm, void __user *argp)
        return kvmi_hook(kvm, &i);
 }
 
+static int kvmi_ioctl_get_feature(void __user *argp, bool *allow, int *id,
+                                 unsigned long *bitmask)
+{
+       struct kvm_introspection_feature feat;
+       int all_bits = -1;
+
+       if (copy_from_user(&feat, argp, sizeof(feat)))
+               return -EFAULT;
+
+       if (feat.id < 0 && feat.id != all_bits)
+               return -EINVAL;
+
+       *allow = !!(feat.allow & 1);
+       *id = feat.id;
+       *bitmask = *id == all_bits ? -1 : BIT(feat.id);
+
+       return 0;
+}
+
+static int kvmi_ioctl_feature(struct kvm *kvm,
+                             bool allow, unsigned long *requested,
+                             size_t off_dest, unsigned int nbits)
+{
+       unsigned long *dest;
+       struct kvmi *ikvm;
+
+       if (bitmap_empty(requested, nbits))
+               return -EINVAL;
+
+       ikvm = kvmi_get(kvm);
+       if (!ikvm)
+               return -EFAULT;
+
+       dest = (unsigned long *)((char *)ikvm + off_dest);
+
+       if (allow)
+               bitmap_or(dest, dest, requested, nbits);
+       else
+               bitmap_andnot(dest, dest, requested, nbits);
+
+       kvmi_put(kvm);
+
+       return 0;
+}
+
+int kvmi_ioctl_event(struct kvm *kvm, void __user *argp)
+{
+       DECLARE_BITMAP(requested, KVMI_NUM_EVENTS);
+       DECLARE_BITMAP(known, KVMI_NUM_EVENTS);
+       bool allow;
+       int err;
+       int id;
+
+       err = kvmi_ioctl_get_feature(argp, &allow, &id, requested);
+       if (err)
+               return err;
+
+       bitmap_from_u64(known, KVMI_KNOWN_EVENTS);
+       bitmap_and(requested, requested, known, KVMI_NUM_EVENTS);
+
+       return kvmi_ioctl_feature(kvm, allow, requested,
+                                 offsetof(struct kvmi, event_allow_mask),
+                                 KVMI_NUM_EVENTS);
+}
+
+int kvmi_ioctl_command(struct kvm *kvm, void __user *argp)
+{
+       DECLARE_BITMAP(requested, KVMI_NUM_COMMANDS);
+       DECLARE_BITMAP(known, KVMI_NUM_COMMANDS);
+       bool allow;
+       int err;
+       int id;
+
+       err = kvmi_ioctl_get_feature(argp, &allow, &id, requested);
+       if (err)
+               return err;
+
+       bitmap_from_u64(known, KVMI_KNOWN_COMMANDS);
+       bitmap_and(requested, requested, known, KVMI_NUM_COMMANDS);
+
+       return kvmi_ioctl_feature(kvm, allow, requested,
+                                 offsetof(struct kvmi, cmd_allow_mask),
+                                 KVMI_NUM_COMMANDS);
+}
+
 void kvmi_create_vm(struct kvm *kvm)
 {
        init_completion(&kvm->kvmi_completed);
diff --git a/virt/kvm/kvmi_int.h b/virt/kvm/kvmi_int.h
index 9bc5205c8714..bd8b539e917a 100644
--- a/virt/kvm/kvmi_int.h
+++ b/virt/kvm/kvmi_int.h
@@ -23,6 +23,54 @@
 #define kvmi_err(ikvm, fmt, ...) \
        kvm_info("%pU ERROR: " fmt, &ikvm->uuid, ## __VA_ARGS__)
 
+#define KVMI_KNOWN_VCPU_EVENTS ( \
+               BIT(KVMI_EVENT_CR) | \
+               BIT(KVMI_EVENT_MSR) | \
+               BIT(KVMI_EVENT_XSETBV) | \
+               BIT(KVMI_EVENT_BREAKPOINT) | \
+               BIT(KVMI_EVENT_HYPERCALL) | \
+               BIT(KVMI_EVENT_PF) | \
+               BIT(KVMI_EVENT_TRAP) | \
+               BIT(KVMI_EVENT_DESCRIPTOR) | \
+               BIT(KVMI_EVENT_PAUSE_VCPU) | \
+               BIT(KVMI_EVENT_SINGLESTEP))
+
+#define KVMI_KNOWN_VM_EVENTS ( \
+               BIT(KVMI_EVENT_CREATE_VCPU) | \
+               BIT(KVMI_EVENT_UNHOOK))
+
+#define KVMI_KNOWN_EVENTS (KVMI_KNOWN_VCPU_EVENTS | KVMI_KNOWN_VM_EVENTS)
+
+#define KVMI_KNOWN_COMMANDS ( \
+               BIT(KVMI_GET_VERSION) | \
+               BIT(KVMI_CHECK_COMMAND) | \
+               BIT(KVMI_CHECK_EVENT) | \
+               BIT(KVMI_GET_GUEST_INFO) | \
+               BIT(KVMI_PAUSE_VCPU) | \
+               BIT(KVMI_CONTROL_VM_EVENTS) | \
+               BIT(KVMI_CONTROL_EVENTS) | \
+               BIT(KVMI_CONTROL_CR) | \
+               BIT(KVMI_CONTROL_MSR) | \
+               BIT(KVMI_CONTROL_VE) | \
+               BIT(KVMI_GET_REGISTERS) | \
+               BIT(KVMI_SET_REGISTERS) | \
+               BIT(KVMI_GET_CPUID) | \
+               BIT(KVMI_GET_XSAVE) | \
+               BIT(KVMI_READ_PHYSICAL) | \
+               BIT(KVMI_WRITE_PHYSICAL) | \
+               BIT(KVMI_INJECT_EXCEPTION) | \
+               BIT(KVMI_GET_PAGE_ACCESS) | \
+               BIT(KVMI_SET_PAGE_ACCESS) | \
+               BIT(KVMI_GET_MAP_TOKEN) | \
+               BIT(KVMI_CONTROL_SPP) | \
+               BIT(KVMI_GET_PAGE_WRITE_BITMAP) | \
+               BIT(KVMI_SET_PAGE_WRITE_BITMAP) | \
+               BIT(KVMI_GET_MTRR_TYPE) | \
+               BIT(KVMI_CONTROL_CMD_RESPONSE) | \
+               BIT(KVMI_GET_VCPU_INFO))
+
+#define KVMI_NUM_COMMANDS KVMI_NEXT_AVAILABLE_COMMAND
+
 #define IKVM(kvm) ((struct kvmi *)((kvm)->kvmi))
 
 struct kvmi {
@@ -32,6 +80,9 @@ struct kvmi {
        struct task_struct *recv;
 
        uuid_t uuid;
+
+       DECLARE_BITMAP(cmd_allow_mask, KVMI_NUM_COMMANDS);
+       DECLARE_BITMAP(event_allow_mask, KVMI_NUM_EVENTS);
 };
 
 /* kvmi_msg.c */
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

Reply via email to