@@ -127,6 +127,7 @@ struct x86_hyper_init {
bool (*msi_ext_dest_id)(void);
void (*init_mem_mapping)(void);
void (*init_after_bootmem)(void);
+ void (*init_heki)(void);
};
/**
@@ -106,4 +106,5 @@ void __init init_hypervisor_platform(void)
x86_hyper_type = h->type;
x86_init.hyper.init_platform();
+ x86_init.hyper.init_heki();
}
@@ -29,6 +29,7 @@
#include <linux/syscore_ops.h>
#include <linux/cc_platform.h>
#include <linux/efi.h>
+#include <linux/heki.h>
#include <asm/timer.h>
#include <asm/cpu.h>
#include <asm/traps.h>
@@ -997,6 +998,60 @@ static bool kvm_sev_es_hcall_finish(struct ghcb *ghcb, struct pt_regs *regs)
}
#endif
+#ifdef CONFIG_HEKI
+
+extern unsigned long cr4_pinned_mask;
+
+/*
+ * TODO: Check SMP policy consistency, e.g. with
+ * this_cpu_read(cpu_tlbstate.cr4)
+ */
+static int kvm_lock_crs(void)
+{
+ unsigned long cr4;
+ int err;
+
+ err = kvm_hypercall3(KVM_HC_LOCK_CR_UPDATE, 0, X86_CR0_WP, 0);
+ if (err)
+ return err;
+
+ cr4 = __read_cr4();
+ err = kvm_hypercall3(KVM_HC_LOCK_CR_UPDATE, 4, cr4 & cr4_pinned_mask,
+ 0);
+ return err;
+}
+
+static struct heki_hypervisor kvm_heki_hypervisor = {
+ .lock_crs = kvm_lock_crs,
+};
+
+static void kvm_init_heki(void)
+{
+ long err;
+
+ if (!kvm_para_available()) {
+ /* Cannot make KVM hypercalls. */
+ return;
+ }
+
+ err = kvm_hypercall3(KVM_HC_LOCK_CR_UPDATE, 0, 0,
+ KVM_LOCK_CR_UPDATE_VERSION);
+ if (err < 1) {
+ /* Ignores host not supporting at least the first version. */
+ return;
+ }
+
+ heki.hypervisor = &kvm_heki_hypervisor;
+}
+
+#else /* CONFIG_HEKI */
+
+static void kvm_init_heki(void)
+{
+}
+
+#endif /* CONFIG_HEKI */
+
const __initconst struct hypervisor_x86 x86_hyper_kvm = {
.name = "KVM",
.detect = kvm_detect,
@@ -1005,6 +1060,7 @@ const __initconst struct hypervisor_x86 x86_hyper_kvm = {
.init.x2apic_available = kvm_para_available,
.init.msi_ext_dest_id = kvm_msi_ext_dest_id,
.init.init_platform = kvm_init_platform,
+ .init.init_heki = kvm_init_heki,
#if defined(CONFIG_AMD_MEM_ENCRYPT)
.runtime.sev_es_hcall_prepare = kvm_sev_es_hcall_prepare,
.runtime.sev_es_hcall_finish = kvm_sev_es_hcall_finish,
@@ -49,6 +49,7 @@ config KVM
select INTERVAL_TREE
select HAVE_KVM_PM_NOTIFIER if PM
select KVM_GENERIC_HARDWARE_ENABLING
+ select HYPERVISOR_SUPPORTS_HEKI
help
Support hosting fully virtualized guest machines using hardware
virtualization extensions. You will need a fairly recent
@@ -9,6 +9,7 @@
#define __HEKI_H__
#include <linux/types.h>
+#include <linux/bug.h>
#include <linux/cache.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -16,15 +17,36 @@
#ifdef CONFIG_HEKI
+/*
+ * A hypervisor that supports Heki will instantiate this structure to
+ * provide hypervisor specific functions for Heki.
+ */
+struct heki_hypervisor {
+ int (*lock_crs)(void); /* Lock control registers. */
+};
+
+/*
+ * If the active hypervisor supports Heki, it will plug its heki_hypervisor
+ * pointer into this heki structure.
+ */
+struct heki {
+ struct heki_hypervisor *hypervisor;
+};
+
+extern struct heki heki;
extern bool heki_enabled;
void heki_early_init(void);
+void heki_late_init(void);
#else /* !CONFIG_HEKI */
static inline void heki_early_init(void)
{
}
+static inline void heki_late_init(void)
+{
+}
#endif /* CONFIG_HEKI */
@@ -1447,6 +1447,7 @@ static int __ref kernel_init(void *unused)
exit_boot_config();
free_initmem();
mark_readonly();
+ heki_late_init();
/*
* Kernel mappings are now finalized - update the userspace page-table
@@ -4,7 +4,7 @@
config HEKI
bool "Hypervisor Enforced Kernel Integrity (Heki)"
- depends on ARCH_SUPPORTS_HEKI
+ depends on ARCH_SUPPORTS_HEKI && HYPERVISOR_SUPPORTS_HEKI
help
This feature enhances guest virtual machine security by taking
advantage of security features provided by the hypervisor for guests.
@@ -17,3 +17,10 @@ config ARCH_SUPPORTS_HEKI
An architecture should select this when it can successfully build
and run with CONFIG_HEKI. That is, it should provide all of the
architecture support required for the HEKI feature.
+
+config HYPERVISOR_SUPPORTS_HEKI
+ bool "Hypervisor support for Heki"
+ help
+ A hypervisor should select this when it can successfully build
+ and run with CONFIG_HEKI. That is, it should provide all of the
+ hypervisor support required for the Heki feature.
@@ -10,6 +10,7 @@
#include "common.h"
bool heki_enabled __ro_after_init = true;
+struct heki heki;
/*
* Must be called after kmem_cache_init().
@@ -21,6 +22,30 @@ __init void heki_early_init(void)
return;
}
pr_warn("Heki is enabled\n");
+
+ if (!heki.hypervisor) {
+ /* This happens for kernels running on bare metal as well. */
+ pr_warn("No support for Heki in the active hypervisor\n");
+ return;
+ }
+ pr_warn("Heki is supported by the active Hypervisor\n");
+}
+
+/*
+ * Must be called after mark_readonly().
+ */
+void heki_late_init(void)
+{
+ struct heki_hypervisor *hypervisor = heki.hypervisor;
+
+ if (!heki_enabled || !heki.hypervisor)
+ return;
+
+ /* Locks control registers so a compromised guest cannot change them. */
+ if (WARN_ON(hypervisor->lock_crs()))
+ return;
+
+ pr_warn("Control registers locked\n");
}
static int __init heki_parse_config(char *str)