Add XO memslot type to create execute-only guest physical memory based on
the RO memslot. Like the RO memslot, disallow changing the memslot type
to/from XO.

In the EPT case ACC_USER_MASK represents the readable bit, so add the
ability for set_spte() to unset this.

This is based in part on a patch by Yu Zhang.

Signed-off-by: Yu Zhang <yu.c.zh...@linux.intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgeco...@intel.com>
---
 arch/x86/kvm/mmu.c             |  9 ++++++++-
 include/uapi/linux/kvm.h       |  1 +
 tools/include/uapi/linux/kvm.h |  1 +
 virt/kvm/kvm_main.c            | 15 ++++++++++++++-
 4 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index e44a8053af78..338cc64cc821 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -2981,6 +2981,8 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
 
        if (pte_access & ACC_USER_MASK)
                spte |= shadow_user_mask;
+       else
+               spte &= ~shadow_user_mask;
 
        if (level > PT_PAGE_TABLE_LEVEL)
                spte |= PT_PAGE_SIZE_MASK;
@@ -3203,6 +3205,11 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t 
gpa, int write,
        int ret;
        gfn_t gfn = gpa >> PAGE_SHIFT;
        gfn_t base_gfn = gfn;
+       struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+       unsigned int pte_access = ACC_ALL;
+
+       if (slot && slot->flags & KVM_MEM_EXECONLY)
+               pte_access = ACC_EXEC_MASK;
 
        if (!VALID_PAGE(vcpu->arch.mmu->root_hpa))
                return RET_PF_RETRY;
@@ -3222,7 +3229,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t gpa, 
int write,
                }
        }
 
-       ret = mmu_set_spte(vcpu, it.sptep, ACC_ALL,
+       ret = mmu_set_spte(vcpu, it.sptep, pte_access,
                           write, level, base_gfn, pfn, prefault,
                           map_writable);
        direct_pte_prefetch(vcpu, it.sptep);
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 5e3f12d5359e..ede487b7b216 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -109,6 +109,7 @@ struct kvm_userspace_memory_region {
  */
 #define KVM_MEM_LOG_DIRTY_PAGES        (1UL << 0)
 #define KVM_MEM_READONLY       (1UL << 1)
+#define KVM_MEM_EXECONLY       (1UL << 2)
 
 /* for KVM_IRQ_LINE */
 struct kvm_irq_level {
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 5e3f12d5359e..ede487b7b216 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -109,6 +109,7 @@ struct kvm_userspace_memory_region {
  */
 #define KVM_MEM_LOG_DIRTY_PAGES        (1UL << 0)
 #define KVM_MEM_READONLY       (1UL << 1)
+#define KVM_MEM_EXECONLY       (1UL << 2)
 
 /* for KVM_IRQ_LINE */
 struct kvm_irq_level {
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index c6a91b044d8d..65087c1d67be 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -865,6 +865,8 @@ static int check_memory_region_flags(const struct 
kvm_userspace_memory_region *m
        valid_flags |= KVM_MEM_READONLY;
 #endif
 
+       valid_flags |= KVM_MEM_EXECONLY;
+
        if (mem->flags & ~valid_flags)
                return -EINVAL;
 
@@ -969,9 +971,12 @@ int __kvm_set_memory_region(struct kvm *kvm,
                if (!old.npages)
                        change = KVM_MR_CREATE;
                else { /* Modify an existing slot. */
+                       const __u8 changeable = KVM_MEM_READONLY
+                                              | KVM_MEM_EXECONLY;
+
                        if ((mem->userspace_addr != old.userspace_addr) ||
                            (npages != old.npages) ||
-                           ((new.flags ^ old.flags) & KVM_MEM_READONLY))
+                           ((new.flags ^ old.flags) & changeable))
                                goto out;
 
                        if (base_gfn != old.base_gfn)
@@ -1356,6 +1361,11 @@ static bool memslot_is_readonly(struct kvm_memory_slot 
*slot)
        return slot->flags & KVM_MEM_READONLY;
 }
 
+static bool memslot_is_execonly(struct kvm_memory_slot *slot)
+{
+       return slot->flags & KVM_MEM_EXECONLY;
+}
+
 static unsigned long __gfn_to_hva_many(struct kvm_memory_slot *slot, gfn_t gfn,
                                       gfn_t *nr_pages, bool write)
 {
@@ -1365,6 +1375,9 @@ static unsigned long __gfn_to_hva_many(struct 
kvm_memory_slot *slot, gfn_t gfn,
        if (memslot_is_readonly(slot) && write)
                return KVM_HVA_ERR_RO_BAD;
 
+       if (memslot_is_execonly(slot) && write)
+               return KVM_HVA_ERR_RO_BAD;
+
        if (nr_pages)
                *nr_pages = slot->npages - (gfn - slot->base_gfn);
 
-- 
2.17.1

Reply via email to