@@ -4699,6 +4699,78 @@ the KVM_CHECK_EXTENSION ioctl() at run-time.
This ioctl is used to free all introspection structures
related to this VM.
+Errors:
+
+ ====== ================================
+ EFAULT the introspection is not enabled
+ ====== ================================
+
+4.127 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
+
+Errors:
+
+ ====== =======================================================
+ EFAULT the introspection is not enabled
+ EINVAL the command is unknown
+ EPERM the command can't be disallowed (e.g. KVMI_GET_VERSION)
+ ====== =======================================================
+
+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)
+
+4.128 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
+
+Errors:
+
+ ====== ====================
+ EFAULT the introspection is not enabled
+ EINVAL the event is unknown
+ ====== ====================
+
+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.)
+
5. The kvm_run structure
========================
@@ -8,6 +8,8 @@ struct kvm;
#include <asm/kvmi_host.h>
+#define KVMI_NUM_COMMANDS KVMI_NUM_MESSAGES
+
struct kvm_introspection {
struct kvm_arch_introspection arch;
struct kvm *kvm;
@@ -16,6 +18,9 @@ struct kvm_introspection {
struct socket *sock;
struct task_struct *recv;
+
+ DECLARE_BITMAP(cmd_allow_mask, KVMI_NUM_COMMANDS);
+ DECLARE_BITMAP(event_allow_mask, KVMI_NUM_EVENTS);
};
#ifdef CONFIG_KVM_INTROSPECTION
@@ -27,6 +32,8 @@ void kvmi_destroy_vm(struct kvm *kvm);
int kvmi_ioctl_hook(struct kvm *kvm, void __user *argp);
int kvmi_ioctl_unhook(struct kvm *kvm);
+int kvmi_ioctl_command(struct kvm *kvm, void __user *argp);
+int kvmi_ioctl_event(struct kvm *kvm, void __user *argp);
#else
@@ -1568,6 +1568,14 @@ struct kvm_introspection_hook {
#define KVM_INTROSPECTION_HOOK _IOW(KVMIO, 0xc3, struct kvm_introspection_hook)
#define KVM_INTROSPECTION_UNHOOK _IO(KVMIO, 0xc4)
+struct kvm_introspection_feature {
+ __u32 allow;
+ __s32 id;
+};
+
+#define KVM_INTROSPECTION_COMMAND _IOW(KVMIO, 0xc5, struct kvm_introspection_feature)
+#define KVM_INTROSPECTION_EVENT _IOW(KVMIO, 0xc6, struct kvm_introspection_feature)
+
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
#define KVM_DEV_ASSIGN_MASK_INTX (1 << 2)
@@ -10,4 +10,12 @@ enum {
KVMI_VERSION = 0x00000001
};
+enum {
+ KVMI_NUM_MESSAGES
+};
+
+enum {
+ KVMI_NUM_EVENTS
+};
+
#endif /* _UAPI__LINUX_KVMI_H */
@@ -33,15 +33,43 @@ void setup_socket(void)
errno, strerror(errno));
}
+static void toggle_event_permission(struct kvm_vm *vm, __s32 id, bool allow)
+{
+ struct kvm_introspection_feature feat = {
+ .allow = allow ? 1 : 0,
+ .id = id
+ };
+ int r;
+
+ r = ioctl(vm->fd, KVM_INTROSPECTION_EVENT, &feat);
+ TEST_ASSERT(r == 0,
+ "KVM_INTROSPECTION_EVENT failed, id %d, errno %d (%s)\n",
+ id, errno, strerror(errno));
+}
+
+static void allow_event(struct kvm_vm *vm, __s32 event_id)
+{
+ toggle_event_permission(vm, event_id, true);
+}
+
static void hook_introspection(struct kvm_vm *vm)
{
+ __s32 all_IDs = -1;
struct kvm_introspection_hook hook = {.fd = Kvm_socket};
+ struct kvm_introspection_feature feat = {.allow = 1, .id = all_IDs};
int r;
r = ioctl(vm->fd, KVM_INTROSPECTION_HOOK, &hook);
TEST_ASSERT(r == 0,
"KVM_INTROSPECTION_HOOK failed, errno %d (%s)\n",
errno, strerror(errno));
+
+ r = ioctl(vm->fd, KVM_INTROSPECTION_COMMAND, &feat);
+ TEST_ASSERT(r == 0,
+ "KVM_INTROSPECTION_COMMAND failed, errno %d (%s)\n",
+ errno, strerror(errno));
+
+ allow_event(vm, all_IDs);
}
static void unhook_introspection(struct kvm_vm *vm)
@@ -184,3 +184,104 @@ void kvmi_destroy_vm(struct kvm *kvm)
{
kvmi_unhook(kvm);
}
+
+static int kvmi_ioctl_get_feature(void __user *argp, bool *allow, int *id,
+ unsigned int nbits)
+{
+ 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;
+
+ if (feat.id > 0 && feat.id >= nbits)
+ return -EINVAL;
+
+ *allow = feat.allow == 1;
+ *id = feat.id;
+
+ return 0;
+}
+
+static void kvmi_control_allowed_events(struct kvm_introspection *kvmi,
+ int id, bool allow)
+{
+ int all_events = -1;
+
+ if (allow) {
+ if (id == all_events)
+ bitmap_fill(kvmi->event_allow_mask, KVMI_NUM_EVENTS);
+ else
+ set_bit(id, kvmi->event_allow_mask);
+ } else {
+ if (id == all_events)
+ bitmap_zero(kvmi->event_allow_mask, KVMI_NUM_EVENTS);
+ else
+ clear_bit(id, kvmi->event_allow_mask);
+ }
+}
+
+int kvmi_ioctl_event(struct kvm *kvm, void __user *argp)
+{
+ struct kvm_introspection *kvmi;
+ int err, id;
+ bool allow;
+
+ err = kvmi_ioctl_get_feature(argp, &allow, &id, KVMI_NUM_EVENTS);
+ if (err)
+ return err;
+
+ mutex_lock(&kvm->kvmi_lock);
+
+ kvmi = KVMI(kvm);
+ if (kvmi)
+ kvmi_control_allowed_events(kvmi, id, allow);
+ else
+ err = -EFAULT;
+
+ mutex_unlock(&kvm->kvmi_lock);
+ return err;
+}
+
+static void kvmi_control_allowed_commands(struct kvm_introspection *kvmi,
+ int id, bool allow)
+{
+ int all_commands = -1;
+
+ if (allow) {
+ if (id == all_commands)
+ bitmap_fill(kvmi->cmd_allow_mask, KVMI_NUM_COMMANDS);
+ else
+ set_bit(id, kvmi->cmd_allow_mask);
+ } else {
+ if (id == all_commands)
+ bitmap_zero(kvmi->cmd_allow_mask, KVMI_NUM_COMMANDS);
+ else
+ clear_bit(id, kvmi->cmd_allow_mask);
+ }
+}
+
+int kvmi_ioctl_command(struct kvm *kvm, void __user *argp)
+{
+ struct kvm_introspection *kvmi;
+ int err, id;
+ bool allow;
+
+ err = kvmi_ioctl_get_feature(argp, &allow, &id, KVMI_NUM_COMMANDS);
+ if (err)
+ return err;
+
+ mutex_lock(&kvm->kvmi_lock);
+
+ kvmi = KVMI(kvm);
+ if (kvmi)
+ kvmi_control_allowed_commands(kvmi, id, allow);
+ else
+ err = -EFAULT;
+
+ mutex_unlock(&kvm->kvmi_lock);
+ return err;
+}
@@ -3590,6 +3590,12 @@ static long kvm_vm_ioctl(struct file *filp,
case KVM_INTROSPECTION_UNHOOK:
r = kvmi_ioctl_unhook(kvm);
break;
+ case KVM_INTROSPECTION_COMMAND:
+ r = kvmi_ioctl_command(kvm, argp);
+ break;
+ case KVM_INTROSPECTION_EVENT:
+ r = kvmi_ioctl_event(kvm, argp);
+ break;
#endif /* CONFIG_KVM_INTROSPECTION */
default:
r = kvm_arch_vm_ioctl(filp, ioctl, arg);
KVM_INTROSPECTION_COMMAND and KVM_INTROSPECTION_EVENTS ioctls are used by userspace to allow access for specific (or all) introspection commands and events. By default, all events and almost all commands are disallowed. Some commands, those querying the introspection capabilities, are always allowed. Signed-off-by: Adalbert Lazăr <alazar@bitdefender.com> --- Documentation/virt/kvm/api.rst | 72 +++++++++++++ include/linux/kvmi_host.h | 7 ++ include/uapi/linux/kvm.h | 8 ++ include/uapi/linux/kvmi.h | 8 ++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 28 +++++ virt/kvm/introspection/kvmi.c | 101 ++++++++++++++++++ virt/kvm/kvm_main.c | 6 ++ 7 files changed, 230 insertions(+)