@@ -1438,6 +1438,9 @@ vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf);
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext);
+int kvm_vm_set_mem_attributes_kernel(struct kvm *kvm, gfn_t start, gfn_t end,
+ unsigned long attributes);
+
void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
struct kvm_memory_slot *slot,
gfn_t gfn_offset,
@@ -2225,6 +2225,9 @@ struct kvm_memory_attributes {
#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3)
+#define KVM_MEMORY_ATTRIBUTES_KERNEL_SHIFT (16)
+#define KVM_MEMORY_ATTRIBUTES_KERNEL_MASK GENMASK(63, KVM_MEMORY_ATTRIBUTES_KERNEL_SHIFT)
+
#define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd)
struct kvm_create_guest_memfd {
@@ -2536,8 +2536,8 @@ static bool kvm_pre_set_memory_attributes(struct kvm *kvm,
}
/* Set @attributes for the gfn range [@start, @end). */
-static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
- unsigned long attributes)
+static int __kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
+ unsigned long attributes, bool userspace)
{
struct kvm_mmu_notifier_range pre_set_range = {
.start = start,
@@ -2559,8 +2559,6 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
void *entry;
int r = 0;
- entry = attributes ? xa_mk_value(attributes) : NULL;
-
mutex_lock(&kvm->slots_lock);
/* Nothing to do if the entire range as the desired attributes. */
@@ -2580,6 +2578,17 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
kvm_handle_gfn_range(kvm, &pre_set_range);
for (i = start; i < end; i++) {
+ /* Maintain kernel/userspace attributes separately. */
+ unsigned long attr = xa_to_value(xa_load(&kvm->mem_attr_array, i));
+
+ if (userspace)
+ attr &= KVM_MEMORY_ATTRIBUTES_KERNEL_MASK;
+ else
+ attr &= ~KVM_MEMORY_ATTRIBUTES_KERNEL_MASK;
+
+ attributes |= attr;
+ entry = attributes ? xa_mk_value(attributes) : NULL;
+
r = xa_err(xa_store(&kvm->mem_attr_array, i, entry,
GFP_KERNEL_ACCOUNT));
KVM_BUG_ON(r, kvm);
@@ -2592,6 +2601,23 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
return r;
}
+
+int kvm_vm_set_mem_attributes_kernel(struct kvm *kvm, gfn_t start, gfn_t end,
+ unsigned long attributes)
+{
+ attributes &= KVM_MEMORY_ATTRIBUTES_KERNEL_MASK;
+
+ return __kvm_vm_set_mem_attributes(kvm, start, end, attributes, false);
+}
+
+static int kvm_vm_set_mem_attributes_userspace(struct kvm *kvm, gfn_t start, gfn_t end,
+ unsigned long attributes)
+{
+ attributes &= ~KVM_MEMORY_ATTRIBUTES_KERNEL_MASK;
+
+ return __kvm_vm_set_mem_attributes(kvm, start, end, attributes, true);
+}
+
static int kvm_vm_ioctl_set_mem_attributes(struct kvm *kvm,
struct kvm_memory_attributes *attrs)
{
@@ -2617,7 +2643,7 @@ static int kvm_vm_ioctl_set_mem_attributes(struct kvm *kvm,
*/
BUILD_BUG_ON(sizeof(attrs->attributes) != sizeof(unsigned long));
- return kvm_vm_set_mem_attributes(kvm, start, end, attrs->attributes);
+ return kvm_vm_set_mem_attributes_userspace(kvm, start, end, attrs->attributes);
}
#endif /* CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES */
Currently userspace can set all KVM memory attributes. Future patches will add new attributes that should only be set by the kernel. Split the attribute space into two parts, one that userspace can set, and one that can only be set by the kernel. This patch introduces two new functions, kvm_vm_set_mem_attributes_kernel() and kvm_vm_set_mem_attributes_user(), whereby each sets the attributes associated with the kernel or with userspace, without clobbering the other's attributes. Since these attributes are stored in an xarray, do the split at bit 16, so that this would still work on 32-bit architectures if needed. Signed-off-by: Fuad Tabba <tabba@google.com> --- include/linux/kvm_host.h | 3 +++ include/uapi/linux/kvm.h | 3 +++ virt/kvm/kvm_main.c | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-)