@@ -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
@@ -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)
@@ -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;
@@ -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);
@@ -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 */
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 <alazar@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(-)