[v1,6/9] KVM: x86: Add Heki hypervisor support
Commit Message
From: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Each supported hypervisor in x86 implements a struct x86_hyper_init to
define the init functions for the hypervisor. Define a new init_heki()
entry point in struct x86_hyper_init. Hypervisors that support Heki
must define this init_heki() function. Call init_heki() of the chosen
hypervisor in init_hypervisor_platform().
Create a heki_hypervisor structure that each hypervisor can fill
with its data and functions. This will allow the Heki feature to work
in a hypervisor agnostic way.
Declare and initialize a "heki_hypervisor" structure for KVM so KVM can
support Heki. Define the init_heki() function for KVM. In init_heki(),
set the hypervisor field in the generic "heki" structure to the KVM
"heki_hypervisor". After this point, generic Heki code can access the
KVM Heki data and functions.
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: Wanpeng Li <wanpengli@tencent.com>
Co-developed-by: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Link: https://lore.kernel.org/r/20230505152046.6575-7-mic@digikod.net
---
arch/x86/include/asm/x86_init.h | 2 +
arch/x86/kernel/cpu/hypervisor.c | 1 +
arch/x86/kernel/kvm.c | 72 ++++++++++++++++++++++++++++++++
arch/x86/kernel/x86_init.c | 1 +
arch/x86/kvm/Kconfig | 1 +
virt/heki/Kconfig | 9 +++-
virt/heki/heki.c | 6 ---
7 files changed, 85 insertions(+), 7 deletions(-)
Comments
On Fri, May 05, 2023 at 05:20:43PM +0200, Mickaël Salaün wrote:
> From: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
>
> Each supported hypervisor in x86 implements a struct x86_hyper_init to
> define the init functions for the hypervisor. Define a new init_heki()
> entry point in struct x86_hyper_init. Hypervisors that support Heki
> must define this init_heki() function. Call init_heki() of the chosen
> hypervisor in init_hypervisor_platform().
>
> Create a heki_hypervisor structure that each hypervisor can fill
> with its data and functions. This will allow the Heki feature to work
> in a hypervisor agnostic way.
>
> Declare and initialize a "heki_hypervisor" structure for KVM so KVM can
> support Heki. Define the init_heki() function for KVM. In init_heki(),
> set the hypervisor field in the generic "heki" structure to the KVM
> "heki_hypervisor". After this point, generic Heki code can access the
> KVM Heki data and functions.
>
[...]
> +static void kvm_init_heki(void)
> +{
> + long err;
> +
> + if (!kvm_para_available())
> + /* Cannot make KVM hypercalls. */
> + return;
> +
> + err = kvm_hypercall3(KVM_HC_LOCK_MEM_PAGE_RANGES, -1, -1, -1);
Why not do a proper version check or capability check here? If the ABI
or supported features ever change then we have something to rely on?
Thanks,
Wei.
On 08/05/2023 23:18, Wei Liu wrote:
> On Fri, May 05, 2023 at 05:20:43PM +0200, Mickaël Salaün wrote:
>> From: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
>>
>> Each supported hypervisor in x86 implements a struct x86_hyper_init to
>> define the init functions for the hypervisor. Define a new init_heki()
>> entry point in struct x86_hyper_init. Hypervisors that support Heki
>> must define this init_heki() function. Call init_heki() of the chosen
>> hypervisor in init_hypervisor_platform().
>>
>> Create a heki_hypervisor structure that each hypervisor can fill
>> with its data and functions. This will allow the Heki feature to work
>> in a hypervisor agnostic way.
>>
>> Declare and initialize a "heki_hypervisor" structure for KVM so KVM can
>> support Heki. Define the init_heki() function for KVM. In init_heki(),
>> set the hypervisor field in the generic "heki" structure to the KVM
>> "heki_hypervisor". After this point, generic Heki code can access the
>> KVM Heki data and functions.
>>
> [...]
>> +static void kvm_init_heki(void)
>> +{
>> + long err;
>> +
>> + if (!kvm_para_available())
>> + /* Cannot make KVM hypercalls. */
>> + return;
>> +
>> + err = kvm_hypercall3(KVM_HC_LOCK_MEM_PAGE_RANGES, -1, -1, -1);
>
> Why not do a proper version check or capability check here? If the ABI
> or supported features ever change then we have something to rely on?
The attributes will indeed get extended, but I wanted to have a simple
proposal for now.
Do you mean to get the version of this hypercall e.g., with a dedicated
flag, like with the
landlock_create_ruleset/LANDLOCK_CREATE_RULESET_VERSION syscall?
>
> Thanks,
> Wei.
@@ -119,6 +119,7 @@ struct x86_init_pci {
* @msi_ext_dest_id: MSI supports 15-bit APIC IDs
* @init_mem_mapping: setup early mappings during init_mem_mapping()
* @init_after_bootmem: guest init after boot allocator is finished
+ * @init_heki: Hypervisor enforced kernel integrity
*/
struct x86_hyper_init {
void (*init_platform)(void);
@@ -127,6 +128,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>
@@ -866,6 +867,45 @@ static void __init kvm_guest_init(void)
hardlockup_detector_disable();
}
+#ifdef CONFIG_HEKI
+
+static int kvm_protect_ranges(struct heki_pa_range *ranges, int num_ranges)
+{
+ size_t size;
+ long err;
+
+ WARN_ON(in_interrupt());
+
+ size = sizeof(ranges[0]) * num_ranges;
+ err = kvm_hypercall3(KVM_HC_LOCK_MEM_PAGE_RANGES, __pa(ranges), size, 0);
+ if (WARN(err, "Failed to enforce memory protection: %ld\n", err))
+ return err;
+
+ return 0;
+}
+
+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_hypercall2(KVM_HC_LOCK_CR_UPDATE, 0, X86_CR0_WP);
+ if (err)
+ return err;
+
+ cr4 = __read_cr4();
+ err = kvm_hypercall2(KVM_HC_LOCK_CR_UPDATE, 4, cr4 & cr4_pinned_mask);
+ return err;
+}
+
+#endif /* CONFIG_HEKI */
+
static noinline uint32_t __kvm_cpuid_base(void)
{
if (boot_cpu_data.cpuid_level < 0)
@@ -999,6 +1039,37 @@ static bool kvm_sev_es_hcall_finish(struct ghcb *ghcb, struct pt_regs *regs)
}
#endif
+#ifdef CONFIG_HEKI
+
+static struct heki_hypervisor kvm_heki_hypervisor = {
+ .protect_ranges = kvm_protect_ranges,
+ .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_MEM_PAGE_RANGES, -1, -1, -1);
+ if (err == -KVM_ENOSYS)
+ /* Ignores host. */
+ 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,
@@ -1007,6 +1078,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,
@@ -114,6 +114,7 @@ struct x86_init_ops x86_init __initdata = {
.msi_ext_dest_id = bool_x86_init_noop,
.init_mem_mapping = x86_init_noop,
.init_after_bootmem = x86_init_noop,
+ .init_heki = x86_init_noop,
},
.acpi = {
@@ -49,6 +49,7 @@ config KVM
select SRCU
select INTERVAL_TREE
select HAVE_KVM_PM_NOTIFIER if PM
+ select HYPERVISOR_SUPPORTS_HEKI
help
Support hosting fully virtualized guest machines using hardware
virtualization extensions. You will need a fairly recent
@@ -6,7 +6,7 @@
config HEKI
bool "Hypervisor Enforced Kernel Integrity (Heki)"
default y
- depends on !JUMP_LABEL && ARCH_SUPPORTS_HEKI
+ depends on !JUMP_LABEL && ARCH_SUPPORTS_HEKI && HYPERVISOR_SUPPORTS_HEKI
select KVM_EXTERNAL_WRITE_TRACKING if KVM
help
This feature enhances guest virtual machine security by taking
@@ -20,3 +20,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.
@@ -91,12 +91,6 @@ void heki_late_init(void)
return;
}
- /*
- * Hypervisor support will be added in the future. When it is, the
- * hypervisor will be used to protect guest kernel memory and
- * control registers.
- */
-
if (!hypervisor) {
/* This happens for kernels running on bare metal as well. */
pr_warn("No hypervisor support\n");