@@ -13,6 +13,7 @@ struct kvmi_monitor_interception {
struct kvmi_interception {
bool restore_interception;
struct kvmi_monitor_interception breakpoint;
+ struct kvmi_monitor_interception cr3w;
};
struct kvm_vcpu_arch_introspection {
@@ -28,6 +29,7 @@ bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg);
bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr,
unsigned long old_value, unsigned long *new_value);
bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu);
+bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable);
#else /* CONFIG_KVM_INTROSPECTION */
@@ -37,6 +39,8 @@ static inline bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr,
unsigned long old_value,
unsigned long *new_value) { return true; }
static inline bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) { return false; }
+static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu,
+ bool enable) { return false; }
#endif /* CONFIG_KVM_INTROSPECTION */
@@ -307,6 +307,59 @@ static void kvmi_arch_disable_bp_intercept(struct kvm_vcpu *vcpu)
vcpu->arch.kvmi->breakpoint.kvm_intercepted = false;
}
+static bool monitor_cr3w_fct_kvmi(struct kvm_vcpu *vcpu, bool enable)
+{
+ vcpu->arch.kvmi->cr3w.kvmi_intercepted = enable;
+
+ if (enable)
+ vcpu->arch.kvmi->cr3w.kvm_intercepted =
+ kvm_x86_ops->cr3_write_intercepted(vcpu);
+ else if (vcpu->arch.kvmi->cr3w.kvm_intercepted)
+ return true;
+
+ return false;
+}
+
+static bool monitor_cr3w_fct_kvm(struct kvm_vcpu *vcpu, bool enable)
+{
+ if (!vcpu->arch.kvmi->cr3w.kvmi_intercepted)
+ return false;
+
+ vcpu->arch.kvmi->cr3w.kvm_intercepted = enable;
+
+ if (!enable)
+ return true;
+
+ return false;
+}
+
+/*
+ * Returns true if one side (kvm or kvmi) tries to disable the CR3 write
+ * interception while the other side is still tracking it.
+ */
+bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable)
+{
+ struct kvmi_interception *arch_vcpui = READ_ONCE(vcpu->arch.kvmi);
+
+ return (arch_vcpui && arch_vcpui->cr3w.monitor_fct(vcpu, enable));
+}
+EXPORT_SYMBOL(kvmi_monitor_cr3w_intercept);
+
+static void kvmi_control_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable)
+{
+ vcpu->arch.kvmi->cr3w.monitor_fct = monitor_cr3w_fct_kvmi;
+ kvm_x86_ops->control_cr3_intercept(vcpu, CR_TYPE_W, enable);
+ vcpu->arch.kvmi->cr3w.monitor_fct = monitor_cr3w_fct_kvm;
+}
+
+static void kvmi_arch_disable_cr3w_intercept(struct kvm_vcpu *vcpu)
+{
+ kvmi_control_cr3w_intercept(vcpu, false);
+
+ vcpu->arch.kvmi->cr3w.kvmi_intercepted = false;
+ vcpu->arch.kvmi->cr3w.kvm_intercepted = false;
+}
+
int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu,
unsigned int event_id, bool enable)
{
@@ -351,6 +404,7 @@ bool kvmi_arch_restore_interception(struct kvm_vcpu *vcpu)
return false;
kvmi_arch_disable_bp_intercept(vcpu);
+ kvmi_arch_disable_cr3w_intercept(vcpu);
return true;
}
@@ -364,8 +418,13 @@ bool kvmi_arch_vcpu_alloc(struct kvm_vcpu *vcpu)
return false;
arch_vcpui->breakpoint.monitor_fct = monitor_bp_fct_kvm;
+ arch_vcpui->cr3w.monitor_fct = monitor_cr3w_fct_kvm;
- /* pair with kvmi_monitor_bp_intercept() */
+ /*
+ * paired with:
+ * - kvmi_monitor_bp_intercept()
+ * - kvmi_monitor_cr3w_intercept()
+ */
smp_wmb();
WRITE_ONCE(vcpu->arch.kvmi, arch_vcpui);
@@ -403,8 +462,7 @@ int kvmi_arch_cmd_vcpu_control_cr(struct kvm_vcpu *vcpu,
case 0:
break;
case 3:
- kvm_x86_ops->control_cr3_intercept(vcpu, CR_TYPE_W,
- req->enable == 1);
+ kvmi_control_cr3w_intercept(vcpu, req->enable == 1);
break;
case 4:
break;
@@ -7497,6 +7497,11 @@ static void svm_control_cr3_intercept(struct kvm_vcpu *vcpu, int type,
{
struct vcpu_svm *svm = to_svm(vcpu);
+#ifdef CONFIG_KVM_INTROSPECTION
+ if ((type & CR_TYPE_W) && kvmi_monitor_cr3w_intercept(vcpu, enable))
+ type &= ~CR_TYPE_W;
+#endif /* CONFIG_KVM_INTROSPECTION */
+
if (type & CR_TYPE_R)
enable ? set_cr_intercept(svm, INTERCEPT_CR3_READ) :
clr_cr_intercept(svm, INTERCEPT_CR3_READ);
@@ -2940,6 +2940,11 @@ static void vmx_control_cr3_intercept(struct kvm_vcpu *vcpu, int type,
struct vcpu_vmx *vmx = to_vmx(vcpu);
u32 cr3_exec_control = 0;
+#ifdef CONFIG_KVM_INTROSPECTION
+ if ((type & CR_TYPE_W) && kvmi_monitor_cr3w_intercept(vcpu, enable))
+ type &= ~CR_TYPE_W;
+#endif /* CONFIG_KVM_INTROSPECTION */
+
if (type & CR_TYPE_R)
cr3_exec_control |= CPU_BASED_CR3_STORE_EXITING;
if (type & CR_TYPE_W)