@@ -50,6 +50,7 @@ config KVM
select HAVE_KVM_PM_NOTIFIER if PM
select KVM_GENERIC_HARDWARE_ENABLING
select HYPERVISOR_SUPPORTS_HEKI
+ select SPARSEMEM
help
Support hosting fully virtualized guest machines using hardware
virtualization extensions. You will need a fairly recent
@@ -9,10 +9,12 @@ endif
include $(srctree)/virt/kvm/Makefile.kvm
+VIRT_LIB = ../../../virt/lib
+
kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \
i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
hyperv.o debugfs.o mmu/mmu.o mmu/page_track.o \
- mmu/spte.o
+ mmu/spte.o $(VIRT_LIB)/kvm_permissions.o
ifdef CONFIG_HYPERV
kvm-y += kvm_onhyperv.o
new file mode 100644
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * KVM guest page permissions - Definitions.
+ *
+ * Copyright © 2023 Microsoft Corporation.
+ */
+#ifndef __KVM_MEM_ATTR_H__
+#define __KVM_MEM_ATTR_H__
+
+#include <linux/kvm_host.h>
+#include <linux/kvm_types.h>
+
+/* clang-format off */
+
+#define MEM_ATTR_READ BIT(0)
+#define MEM_ATTR_WRITE BIT(1)
+#define MEM_ATTR_EXEC BIT(2)
+#define MEM_ATTR_IMMUTABLE BIT(3)
+
+#define MEM_ATTR_PROT ( \
+ MEM_ATTR_READ | \
+ MEM_ATTR_WRITE | \
+ MEM_ATTR_EXEC | \
+ MEM_ATTR_IMMUTABLE)
+
+/* clang-format on */
+
+int kvm_permissions_set(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end,
+ unsigned long heki_attr);
+unsigned long kvm_permissions_get(struct kvm *kvm, gfn_t gfn);
+
+#endif /* __KVM_MEM_ATTR_H__ */
@@ -2319,6 +2319,11 @@ struct kvm_memory_attributes {
#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3)
+#define KVM_MEMORY_ATTRIBUTE_HEKI_READ (1ULL << 4)
+#define KVM_MEMORY_ATTRIBUTE_HEKI_WRITE (1ULL << 5)
+#define KVM_MEMORY_ATTRIBUTE_HEKI_EXEC (1ULL << 6)
+#define KVM_MEMORY_ATTRIBUTE_HEKI_IMMUTABLE (1ULL << 7)
+
#define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd)
struct kvm_create_guest_memfd {
@@ -5,6 +5,7 @@
config HEKI
bool "Hypervisor Enforced Kernel Integrity (Heki)"
depends on ARCH_SUPPORTS_HEKI && HYPERVISOR_SUPPORTS_HEKI
+ select KVM_GENERIC_MEMORY_ATTRIBUTES
help
This feature enhances guest virtual machine security by taking
advantage of security features provided by the hypervisor for guests.
new file mode 100644
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KVM guest page permissions - functions.
+ *
+ * Copyright © 2023 Microsoft Corporation.
+ */
+#include <linux/kvm_host.h>
+#include <linux/kvm_mem_attr.h>
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+
+#define pr_fmt(fmt) "kvm: heki: " fmt
+
+/* clang-format off */
+
+static unsigned long kvm_default_permissions =
+ MEM_ATTR_READ |
+ MEM_ATTR_WRITE |
+ MEM_ATTR_EXEC;
+
+static unsigned long kvm_memory_attributes_heki =
+ KVM_MEMORY_ATTRIBUTE_HEKI_READ |
+ KVM_MEMORY_ATTRIBUTE_HEKI_WRITE |
+ KVM_MEMORY_ATTRIBUTE_HEKI_EXEC |
+ KVM_MEMORY_ATTRIBUTE_HEKI_IMMUTABLE;
+
+/* clang-format on */
+
+static unsigned long heki_attr_to_kvm_attr(unsigned long heki_attr)
+{
+ unsigned long kvm_attr = 0;
+
+ if (WARN_ON_ONCE((heki_attr | MEM_ATTR_PROT) != MEM_ATTR_PROT))
+ return 0;
+
+ if (heki_attr & MEM_ATTR_READ)
+ kvm_attr |= KVM_MEMORY_ATTRIBUTE_HEKI_READ;
+ if (heki_attr & MEM_ATTR_WRITE)
+ kvm_attr |= KVM_MEMORY_ATTRIBUTE_HEKI_WRITE;
+ if (heki_attr & MEM_ATTR_EXEC)
+ kvm_attr |= KVM_MEMORY_ATTRIBUTE_HEKI_EXEC;
+ if (heki_attr & MEM_ATTR_IMMUTABLE)
+ kvm_attr |= KVM_MEMORY_ATTRIBUTE_HEKI_IMMUTABLE;
+ return kvm_attr;
+}
+
+static unsigned long kvm_attr_to_heki_attr(unsigned long kvm_attr)
+{
+ unsigned long heki_attr = 0;
+
+ if (kvm_attr & KVM_MEMORY_ATTRIBUTE_HEKI_READ)
+ heki_attr |= MEM_ATTR_READ;
+ if (kvm_attr & KVM_MEMORY_ATTRIBUTE_HEKI_WRITE)
+ heki_attr |= MEM_ATTR_WRITE;
+ if (kvm_attr & KVM_MEMORY_ATTRIBUTE_HEKI_EXEC)
+ heki_attr |= MEM_ATTR_EXEC;
+ if (kvm_attr & KVM_MEMORY_ATTRIBUTE_HEKI_IMMUTABLE)
+ heki_attr |= MEM_ATTR_IMMUTABLE;
+ return heki_attr;
+}
+
+unsigned long kvm_permissions_get(struct kvm *kvm, gfn_t gfn)
+{
+ unsigned long kvm_attr = 0;
+
+ /*
+ * Retrieve the permissions for a guest page. If not present (i.e., no
+ * attribute), then return default permissions (RWX). This means
+ * setting permissions to 0 resets them to RWX. We might want to
+ * revisit that in a future version.
+ */
+ kvm_attr = kvm_get_memory_attributes(kvm, gfn);
+ if (kvm_attr)
+ return kvm_attr_to_heki_attr(kvm_attr);
+ else
+ return kvm_default_permissions;
+}
+EXPORT_SYMBOL_GPL(kvm_permissions_get);
+
+int kvm_permissions_set(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end,
+ unsigned long heki_attr)
+{
+ if ((heki_attr | MEM_ATTR_PROT) != MEM_ATTR_PROT)
+ return -EINVAL;
+
+ if (gfn_end <= gfn_start)
+ return -EINVAL;
+
+ if (kvm_range_has_memory_attributes(kvm, gfn_start, gfn_end,
+ KVM_MEMORY_ATTRIBUTE_HEKI_IMMUTABLE,
+ false)) {
+ pr_warn_ratelimited(
+ "Guest tried to change immutable permission for GFNs %llx-%llx\n",
+ gfn_start, gfn_end);
+ return -EPERM;
+ }
+
+ return kvm_vm_set_mem_attributes(kvm, gfn_start, gfn_end,
+ heki_attr_to_kvm_attr(heki_attr),
+ kvm_memory_attributes_heki);
+}
+EXPORT_SYMBOL_GPL(kvm_permissions_set);