From: David Woodhouse <[email protected]>

The pieces the VSM contract tests need:

- KVM_SET_PLANE_MEMORY_OVERLAY maps one page of userspace memory over
  a GPA for a single plane. TLFS overlay pages (SynIC SIMP in
  particular) show each VTL a private page while the other VTLs keep
  seeing the guest RAM underneath; per-plane EPT roots make a
  per-plane pfn override in the fault path sufficient. Mappings for a
  plane with overlays stay 4KiB so a neighbouring fault's huge page
  cannot span an overlaid gfn, and kvm_tdp_mmu_recover_huge_pages()
  skips nonzero-plane roots so a later promotion cannot widen the
  plane's attribute or overlay clamps back out (planes keep no lpage
  tracking by design; their fault path rebuilds each mapping at the
  largest level the plane allows). The overlay update runs under the
  kvm_mmu_invalidate_begin()/end() protocol with the gfn registered,
  as the mmu notifiers do, so a concurrent lockless fault cannot
  install an SPTE for the old backing after the zap.

- SynIC message delivery reads and writes the overlay through the same
  lookup, and fails with -EFAULT when a higher plane's SIMP page has
  no overlay installed yet, rather than falling through to
  kvm_vcpu_write_guest_page() and corrupting underlay RAM that plane 0
  owns; the sender retries once the overlay appears. A non-zero
  plane's SynIC exits carry the plane instead of being suppressed, so
  the VMM can keep its plane-0 device state separate.

- A plane-protection fault reflected as KVM_EXIT_MEMORY_FAULT now
  carries the guest-linear address and the faulting instruction's
  length and bytes (via VM_EXIT_INSTRUCTION_LEN and a guest-virt
  read), which the TLFS GPA-intercept message requires. LLDT joins
  LGDT/LIDT in the descriptor-write divert, reporting the written
  selector.

Signed-off-by: David Woodhouse <[email protected]>
Assisted-by: LLM
---
 Documentation/virt/kvm/api.rst |  62 ++++++++
 arch/x86/kvm/hyperv.c          |  82 +++++++---
 arch/x86/kvm/mmu/mmu.c         |  29 +++-
 arch/x86/kvm/mmu/tdp_mmu.c     |  23 ++-
 arch/x86/kvm/vmx/vmx.c         |  51 ++++++-
 arch/x86/kvm/x86.c             |  14 ++
 include/linux/kvm_host.h       |  17 +++
 include/uapi/linux/kvm.h       |  17 +++
 virt/kvm/kvm_main.c            | 271 +++++++++++++++++++++++++++++++++
 9 files changed, 541 insertions(+), 25 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 3ef57b3b050b..215547802377 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6657,6 +6657,38 @@ stop policy.
 The run state is distinct from KVM_GET/SET_MP_STATE, which describes
 the architectural state of a vCPU within its plane.
 
+4.149 KVM_SET_PLANE_MEMORY_OVERLAY
+----------------------------------
+
+:Capability: KVM_CAP_PLANE_MEMORY_OVERLAYS
+:Architectures: x86
+:Type: vm ioctl
+:Parameters: struct kvm_plane_memory_overlay (in)
+:Returns: 0 on success, <0 on error
+
+::
+
+  struct kvm_plane_memory_overlay {
+       __u32 plane;
+       __u32 flags;
+       __u64 gfn;
+       __u64 uaddr;            /* page-aligned; 0 removes the overlay */
+  };
+
+Installs, replaces or removes a memory overlay for a single guest page
+frame on one plane.  While an overlay is installed, translations of
+'gfn' for vCPUs of 'plane' resolve to the (pinned) page at 'uaddr' in
+the calling process instead of to the memslot backing; other planes are
+unaffected.  Hyper-V VSM uses this for per-VTL overlay pages such as
+the SynIC message and event-flags pages.
+
+'flags' must be zero and 'uaddr' must be page-aligned.  At most
+KVM_CHECK_EXTENSION(KVM_CAP_PLANE_MEMORY_OVERLAYS) distinct gfns can
+carry an overlay per plane at any one time; further installs fail with
+-ENOSPC until an overlay is removed.  An overlay cannot be installed
+over a private or guest_memfd-only gfn (-EINVAL).  Passing 'uaddr' == 0
+removes the overlay for 'gfn', if any.
+
 5. The kvm_run structure
 ========================
 
@@ -7387,9 +7419,15 @@ spec refer, https://github.com/riscv/riscv-sbi-doc.
   #define KVM_MEMORY_EXIT_FLAG_WRITE    (1ULL << 1)
   #define KVM_MEMORY_EXIT_FLAG_EXEC     (1ULL << 2)
   #define KVM_MEMORY_EXIT_FLAG_PRIVATE (1ULL << 3)
+  #define KVM_MEMORY_EXIT_FLAG_GVA_VALID       (1ULL << 4)
                        __u64 flags;
                        __u64 gpa;
                        __u64 size;
+                       /* set on plane-protection faults when available */
+                       __u64 gva;
+                       __u32 insn_len;
+                       __u8  insn_bytes[16];
+                       __u32 pad;
                } memory_fault;
 
 KVM_EXIT_MEMORY_FAULT indicates the vCPU has encountered a memory fault that
@@ -7402,6 +7440,14 @@ describes properties of the faulting access that are 
likely pertinent:
  - KVM_MEMORY_EXIT_FLAG_PRIVATE - When set, indicates the memory fault occurred
    on a private memory access.  When clear, indicates the fault occurred on a
    shared access.
+ - KVM_MEMORY_EXIT_FLAG_GVA_VALID - When set, 'gva' holds the faulting guest
+   virtual address.
+
+The 'gva', 'insn_len' and 'insn_bytes' fields exist only on kernels
+which advertise KVM_CAP_PLANE_MEMORY_OVERLAYS (see section 8.48).  On
+plane memory-protection faults, 'insn_len' and 'insn_bytes' carry a
+best-effort copy of the faulting instruction when KVM could fetch it;
+an 'insn_len' of 0 means no instruction bytes are available.
 
 Note!  KVM_EXIT_MEMORY_FAULT is unique among all KVM exit reasons in that it
 accompanies a return code of '-1', not '0'!  errno will always be set to EFAULT
@@ -9676,6 +9722,22 @@ take care to differentiate between these cases.
 The presence of this capability indicates that the nested KVM guest can
 start in ESA mode.
 
+8.48 KVM_CAP_PLANE_MEMORY_OVERLAYS
+----------------------------------
+
+:Architectures: x86
+
+A positive return from KVM_CHECK_EXTENSION(KVM_CAP_PLANE_MEMORY_OVERLAYS)
+announces two things:
+
+ - The KVM_SET_PLANE_MEMORY_OVERLAY vm ioctl (section 4.149).  The
+   returned value is the number of gfns which can carry an overlay
+   simultaneously on each plane.
+
+ - The extended KVM_EXIT_MEMORY_FAULT layout: 'memory_fault' carries
+   the additional 'gva', 'insn_len' and 'insn_bytes' fields, with
+   KVM_MEMORY_EXIT_FLAG_GVA_VALID qualifying 'gva' (see section 5).
+
 9. Known KVM API problems
 =========================
 
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 4551e011bf56..d4e44ae3f6e8 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -247,16 +247,8 @@ static void synic_exit(struct kvm_vcpu_hv_synic *synic, 
u32 msr)
        struct kvm_vcpu *vcpu = hv_synic_to_vcpu(synic);
        struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
 
-       /*
-        * Userspace's SynIC device models plane 0 only; a higher plane's
-        * exit would be mistaken for a plane-0 page move and relocate the
-        * overlay the VMM keeps for SIMP/SIEFP.  Higher-plane SynICs are
-        * handled fully in-kernel.
-        */
-       if (vcpu->plane_level)
-               return;
-
        hv_vcpu->exit.type = KVM_EXIT_HYPERV_SYNIC;
+       hv_vcpu->exit.plane = vcpu->plane_level;
        hv_vcpu->exit.u.synic.msr = msr;
        hv_vcpu->exit.u.synic.control = synic->control;
        hv_vcpu->exit.u.synic.evt_page = synic->evt_page;
@@ -358,6 +350,7 @@ static void syndbg_exit(struct kvm_vcpu *vcpu, u32 msr)
        struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
 
        hv_vcpu->exit.type = KVM_EXIT_HYPERV_SYNDBG;
+       hv_vcpu->exit.plane = vcpu->plane_level;
        hv_vcpu->exit.u.syndbg.msr = msr;
        hv_vcpu->exit.u.syndbg.control = syndbg->control.control;
        hv_vcpu->exit.u.syndbg.send_page = syndbg->control.send_page;
@@ -783,6 +776,51 @@ static int stimer_get_count(struct kvm_vcpu_hv_stimer 
*stimer, u64 *pcount)
        return 0;
 }
 
+/*
+ * SIMP accessors: a plane overlay page, when installed by userspace,
+ * carries the live message page for this plane; the memslot holds only
+ * the underlay the other planes see.
+ */
+static int synic_msg_page_read(struct kvm_vcpu *vcpu, gfn_t gfn, void *data,
+                              int offset, int len)
+{
+       unsigned long uaddr = kvm_plane_overlay_uaddr(vcpu->plane, gfn);
+
+       if (uaddr)
+               return copy_from_user(data, (void __user *)(uaddr + offset),
+                                     len) ? -EFAULT : 0;
+       /*
+        * Mirror the write side: without an overlay a higher plane's
+        * message page is backed by underlay memory that plane 0 controls.
+        * Deciding deliverability from plane-0-writable bytes would let a
+        * lower VTL plant a non-HVMSG_NONE type and silently swallow a
+        * no_retry (periodic stimer) message; fail so the delivery is
+        * retried once the overlay appears.
+        */
+       if (vcpu->plane_level)
+               return -EFAULT;
+       return kvm_vcpu_read_guest_page(vcpu, gfn, data, offset, len);
+}
+
+static int synic_msg_page_write(struct kvm_vcpu *vcpu, gfn_t gfn,
+                               const void *data, int offset, int len)
+{
+       unsigned long uaddr = kvm_plane_overlay_uaddr(vcpu->plane, gfn);
+
+       if (uaddr)
+               return copy_to_user((void __user *)(uaddr + offset), data,
+                                   len) ? -EFAULT : 0;
+       /*
+        * Never write a higher plane's messages into guest RAM: without an
+        * overlay installed that memory is the underlay every other plane
+        * sees, and silently corrupting plane-0-owned pages is far worse
+        * than retrying the message once the overlay appears.
+        */
+       if (vcpu->plane_level)
+               return -EFAULT;
+       return kvm_vcpu_write_guest_page(vcpu, gfn, data, offset, len);
+}
+
 static int synic_deliver_msg(struct kvm_vcpu_hv_synic *synic, u32 sint,
                             struct hv_message *src_msg, bool no_retry)
 {
@@ -803,10 +841,10 @@ static int synic_deliver_msg(struct kvm_vcpu_hv_synic 
*synic, u32 sint,
         * is only called in vcpu context so the entire update is atomic from
         * guest POV and thus the exact order here doesn't matter.
         */
-       r = kvm_vcpu_read_guest_page(vcpu, msg_page_gfn, &hv_hdr.message_type,
-                                    msg_off + offsetof(struct hv_message,
-                                                       header.message_type),
-                                    sizeof(hv_hdr.message_type));
+       r = synic_msg_page_read(vcpu, msg_page_gfn, &hv_hdr.message_type,
+                               msg_off + offsetof(struct hv_message,
+                                                  header.message_type),
+                               sizeof(hv_hdr.message_type));
        if (r < 0)
                return r;
 
@@ -815,20 +853,20 @@ static int synic_deliver_msg(struct kvm_vcpu_hv_synic 
*synic, u32 sint,
                        return 0;
 
                hv_hdr.message_flags.msg_pending = 1;
-               r = kvm_vcpu_write_guest_page(vcpu, msg_page_gfn,
-                                             &hv_hdr.message_flags,
-                                             msg_off +
-                                             offsetof(struct hv_message,
-                                                      header.message_flags),
-                                             sizeof(hv_hdr.message_flags));
+               r = synic_msg_page_write(vcpu, msg_page_gfn,
+                                        &hv_hdr.message_flags,
+                                        msg_off +
+                                        offsetof(struct hv_message,
+                                                 header.message_flags),
+                                        sizeof(hv_hdr.message_flags));
                if (r < 0)
                        return r;
                return -EAGAIN;
        }
 
-       r = kvm_vcpu_write_guest_page(vcpu, msg_page_gfn, src_msg, msg_off,
-                                     sizeof(src_msg->header) +
-                                     src_msg->header.payload_size);
+       r = synic_msg_page_write(vcpu, msg_page_gfn, src_msg, msg_off,
+                                sizeof(src_msg->header) +
+                                src_msg->header.payload_size);
        if (r < 0)
                return r;
 
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index cdebb5c324fe..4b11631d9e99 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -4620,7 +4620,13 @@ static int kvm_mmu_faultin_pfn_gmem(struct kvm_vcpu 
*vcpu,
        if (fault->slot->flags & KVM_MEM_READONLY)
                fault->host_access &= ~ACC_WRITE_MASK;
 
-       fault->max_level = kvm_max_level_for_order(max_order);
+       /*
+        * Only ever narrow max_level: the overlay check in
+        * __kvm_mmu_faultin_pfn() may already have clamped it to 4KiB, and
+        * a huge gmem mapping must not span an overlaid gfn.
+        */
+       fault->max_level = min(fault->max_level,
+                              kvm_max_level_for_order(max_order));
        return RET_PF_CONTINUE;
 }
 
@@ -4682,8 +4688,29 @@ static int __kvm_mmu_faultin_pfn(struct kvm_vcpu *vcpu,
                                 struct kvm_page_fault *fault)
 {
        unsigned int foll = fault->write ? FOLL_WRITE : 0;
+       struct page *overlay;
        bool writable;
 
+       /*
+        * A plane overlay page (VSM SynIC SIMP/SIEFP etc.) replaces the
+        * memslot translation for this plane only.  The page is pinned
+        * for the overlay's lifetime, so no refcounting here.
+        */
+       /*
+        * Keep this plane's mappings at 4KiB while it has overlays: a huge
+        * mapping established by a neighbouring gfn's fault would span the
+        * overlaid gfn and bypass the override below.
+        */
+       if (READ_ONCE(vcpu->plane->nr_overlays)) {
+               fault->max_level = PG_LEVEL_4K;
+
+               overlay = kvm_plane_overlay_page(vcpu->plane, fault->gfn);
+               if (overlay) {
+                       fault->pfn = page_to_pfn(overlay);
+                       return RET_PF_CONTINUE;
+               }
+       }
+
        if (fault->is_private || kvm_memslot_is_gmem_only(fault->slot))
                return kvm_mmu_faultin_pfn_gmem(vcpu, fault);
 
diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c
index 1e56a4784160..d5d0b2bc547c 100644
--- a/arch/x86/kvm/mmu/tdp_mmu.c
+++ b/arch/x86/kvm/mmu/tdp_mmu.c
@@ -1871,8 +1871,29 @@ void kvm_tdp_mmu_recover_huge_pages(struct kvm *kvm,
        struct kvm_mmu_page *root;
 
        lockdep_assert_held_read(&kvm->mmu_lock);
-       for_each_valid_tdp_mmu_root_yield_safe(kvm, root, slot->as_id)
+       for_each_valid_tdp_mmu_root_yield_safe(kvm, root, slot->as_id) {
+               /*
+                * kvm_mmu_max_mapping_level() consults only plane-0 state
+                * (lpage_info and plane-0 private attributes).  Nonzero
+                * planes carry per-plane memory attributes and memory
+                * overlays whose fault-time 4KiB clamps a huge SPTE would
+                * undo, extending one page's protections or overlay
+                * backing over gfns the plane maps differently.  Leave
+                * those roots alone; their fault path rebuilds mappings at
+                * the largest level the plane allows.
+                *
+                * Plane 0 can carry overlays too (kvm->planes[0] always
+                * exists and KVM_SET_PLANE_MEMORY_OVERLAY accepts plane 0),
+                * and the same reasoning applies: a recovered huge SPTE
+                * would either span an overlaid gfn with the memslot
+                * backing, or huge-align the pinned overlay page's pfn and
+                * map ~2MiB of unrelated host memory around it.
+                */
+               if (root->role.plane ||
+                   READ_ONCE(kvm->planes[root->role.plane]->nr_overlays))
+                       continue;
                recover_huge_pages_range(kvm, root, slot);
+       }
 }
 
 /*
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e1c5f1608b09..248d799b46e9 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -70,6 +70,7 @@
 #include "vmcs12.h"
 #include "vmx.h"
 #include "x86.h"
+#include "kvm_emulate.h"
 #include "x86_ops.h"
 #include "smm.h"
 #include "vmx_onhyperv.h"
@@ -5949,6 +5950,7 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
 {
        unsigned long exit_qualification = vmx_get_exit_qual(vcpu);
        gpa_t gpa;
+       int ret;
 
        /*
         * EPT violation happened while executing iret from NMI,
@@ -5975,7 +5977,54 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
        if (unlikely(allow_smaller_maxphyaddr && !kvm_vcpu_is_legal_gpa(vcpu, 
gpa)))
                return kvm_emulate_instruction(vcpu, 0);
 
-       return __vmx_handle_ept_violation(vcpu, gpa, exit_qualification);
+       ret = __vmx_handle_ept_violation(vcpu, gpa, exit_qualification);
+
+       /*
+        * A plane-protection fault reflected to userspace becomes a TLFS
+        * GPA intercept message, which carries the guest-linear address
+        * and the faulting instruction's bytes.  Harvest them while the
+        * exit state is still at hand.
+        */
+       if (ret == -EFAULT &&
+           vcpu->run->exit_reason == KVM_EXIT_MEMORY_FAULT &&
+           (vcpu->run->memory_fault.flags & (KVM_MEMORY_EXIT_FLAG_READ |
+                                             KVM_MEMORY_EXIT_FLAG_WRITE |
+                                             KVM_MEMORY_EXIT_FLAG_EXEC))) {
+               unsigned int len;
+
+               if (exit_qualification & EPT_VIOLATION_GVA_IS_VALID) {
+                       vcpu->run->memory_fault.gva =
+                               vmcs_readl(GUEST_LINEAR_ADDRESS);
+                       vcpu->run->memory_fault.flags |=
+                               KVM_MEMORY_EXIT_FLAG_GVA_VALID;
+               }
+               /*
+                * Architecturally undefined for EPT violations, but the
+                * hardware provides the faulting (for a fetch: the
+                * fetching) instruction's length here, and the TLFS
+                * message carries exactly that.
+                */
+               len = vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
+               len = min_t(unsigned int, len,
+                           sizeof(vcpu->run->memory_fault.insn_bytes));
+               vcpu->run->memory_fault.insn_len = len;
+               if (!(exit_qualification & EPT_VIOLATION_ACC_INSTR)) {
+                       struct x86_exception e;
+
+                       /*
+                        * kvm_read_guest_virt() takes a linear address: RIP
+                        * is CS-relative, and the two coincide only when
+                        * CS.base is zero (64-bit mode, or a flat 32-bit
+                        * code segment).
+                        */
+                       if (kvm_read_guest_virt(vcpu, kvm_get_linear_rip(vcpu),
+                                               
vcpu->run->memory_fault.insn_bytes,
+                                               len, &e))
+                               memset(vcpu->run->memory_fault.insn_bytes, 0,
+                                      
sizeof(vcpu->run->memory_fault.insn_bytes));
+               }
+       }
+       return ret;
 }
 
 static int handle_ept_misconfig(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 4b12ec7ee100..10e14ab3e2ca 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8858,8 +8858,10 @@ static bool hv_divert_dt_write(struct x86_emulate_ctxt 
*ctxt, u32 hv_reg,
 
 #define HV_CR_INTERCEPT_GDTR_WRITE     BIT_ULL(15)
 #define HV_CR_INTERCEPT_IDTR_WRITE     BIT_ULL(16)
+#define HV_CR_INTERCEPT_LDTR_WRITE     BIT_ULL(17)
 #define HV_X64_REGISTER_IDTR_ID                0x00070000
 #define HV_X64_REGISTER_GDTR_ID                0x00070001
+#define HV_X64_REGISTER_LDTR_ID                0x00060006
 
 static void emulator_set_gdt(struct x86_emulate_ctxt *ctxt, struct desc_ptr 
*dt)
 {
@@ -8947,6 +8949,18 @@ static void emulator_set_segment(struct x86_emulate_ctxt 
*ctxt, u16 selector,
        var.unusable = !var.present;
        var.padding = 0;
 
+       if (seg == VCPU_SREG_LDTR) {
+               struct desc_ptr sel_as_dt = {
+                       .address = selector,
+                       .size = 0,
+               };
+
+               /* The intercept message carries the written selector. */
+               if (hv_divert_dt_write(ctxt, HV_X64_REGISTER_LDTR_ID,
+                                      &sel_as_dt, HV_CR_INTERCEPT_LDTR_WRITE))
+                       return;
+       }
+
        kvm_set_segment(vcpu, &var, seg);
        return;
 }
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index a76615f2c3dc..cb8c4798df12 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -927,9 +927,26 @@ struct kvm_plane {
        struct xarray mem_attrs;
 #endif
 
+       /*
+        * VSM overlay pages: per-plane gfn -> pinned user page overriding
+        * the memslot translation for this plane only.  Written under
+        * kvm->slots_lock; readers tolerate a transient stale view, the
+        * post-update zap makes the EPT coherent.
+        */
+#define KVM_PLANE_MAX_OVERLAYS 64
+       struct kvm_plane_overlay {
+               gfn_t gfn;
+               unsigned long uaddr;
+               struct page *page;
+       } overlays[KVM_PLANE_MAX_OVERLAYS];
+       int nr_overlays;
+
        struct kvm_arch_plane arch;
 };
 
+struct page *kvm_plane_overlay_page(struct kvm_plane *plane, gfn_t gfn);
+unsigned long kvm_plane_overlay_uaddr(struct kvm_plane *plane, gfn_t gfn);
+
 struct kvm {
 #ifdef KVM_HAVE_MMU_RWLOCK
        rwlock_t mmu_lock;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 9e453f215e68..843cef8f4cbf 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -480,9 +480,15 @@ struct kvm_run {
 #define KVM_MEMORY_EXIT_FLAG_WRITE      (1ULL << 1)
 #define KVM_MEMORY_EXIT_FLAG_EXEC       (1ULL << 2)
 #define KVM_MEMORY_EXIT_FLAG_PRIVATE   (1ULL << 3)
+#define KVM_MEMORY_EXIT_FLAG_GVA_VALID (1ULL << 4)
                        __u64 flags;
                        __u64 gpa;
                        __u64 size;
+                       /* set on plane-protection faults when available */
+                       __u64 gva;
+                       __u32 insn_len;
+                       __u8  insn_bytes[16];
+                       __u32 pad;
                } memory_fault;
                /* KVM_EXIT_TDX */
                struct {
@@ -1038,6 +1044,7 @@ struct kvm_enable_cap {
 #define KVM_CAP_HCALL_FAULT_EXIT 252
 #define KVM_CAP_HYPERV_VTL_SWITCH 253
 #define KVM_CAP_HYPERV_REG_INTERCEPT 254
+#define KVM_CAP_PLANE_MEMORY_OVERLAYS 257
 
 struct kvm_irq_routing_irqchip {
        __u32 irqchip;
@@ -1394,6 +1401,16 @@ struct kvm_s390_keyop {
 #define KVM_CREATE_PLANE         _IO(KVMIO, 0xe4)
 #define KVM_GET_PLANE_RUN_STATE          _IOR(KVMIO, 0xe5, __u32)
 #define KVM_SET_PLANE_RUN_STATE          _IOW(KVMIO, 0xe6, __u32)
+
+/* Map one page of userspace memory over a GPA for a single plane (VSM
+ * overlay pages: SynIC SIMP/SIEFP, hypercall page, VP assist page). */
+struct kvm_plane_memory_overlay {
+       __u32 plane;
+       __u32 flags;
+       __u64 gfn;
+       __u64 uaddr;            /* page-aligned; 0 removes the overlay */
+};
+#define KVM_SET_PLANE_MEMORY_OVERLAY     _IOW(KVMIO, 0xe7, struct 
kvm_plane_memory_overlay)
 #define   KVM_PLANE_RUN_STATE_STOPPED  0
 #define   KVM_PLANE_RUN_STATE_RUNNABLE 1
 #define   KVM_PLANE_RUN_STATE_KERNEL_STOPPED   2
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 4197496f7176..5856672bbf6d 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1251,6 +1251,14 @@ static struct kvm_plane *kvm_create_plane(struct kvm 
*kvm, unsigned plane_level)
 
 static void kvm_destroy_one_plane(struct kvm_plane *plane)
 {
+       int i;
+
+       for (i = 0; i < plane->nr_overlays; i++)
+               if (plane->overlays[i].page)
+                       unpin_user_pages_dirty_lock(&plane->overlays[i].page,
+                                                   1, true);
+       plane->nr_overlays = 0;
+
 #ifdef CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES
        xa_destroy(&plane->mem_attrs);
 #endif
@@ -5629,6 +5637,8 @@ static int kvm_vm_ioctl_check_extension_generic(struct 
kvm *kvm, long arg)
                 * VM actually has.
                 */
                return KVM_MAX_PLANES;
+       case KVM_CAP_PLANE_MEMORY_OVERLAYS:
+               return KVM_PLANE_MAX_OVERLAYS;
        default:
                break;
        }
@@ -5896,6 +5906,258 @@ do {                                                    
                        \
                     sizeof_field(struct kvm_userspace_memory_region2, field)); 
\
 } while (0)
 
+struct page *kvm_plane_overlay_page(struct kvm_plane *plane, gfn_t gfn)
+{
+       int i, n = READ_ONCE(plane->nr_overlays);
+
+       /* pairs with the smp_wmb() publishing a new entry */
+       smp_rmb();
+       for (i = 0; i < n; i++) {
+               if (READ_ONCE(plane->overlays[i].gfn) != gfn)
+                       continue;
+               /* pairs with the smp_store_release() updating an entry */
+               return smp_load_acquire(&plane->overlays[i].page);
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_plane_overlay_page);
+
+unsigned long kvm_plane_overlay_uaddr(struct kvm_plane *plane, gfn_t gfn)
+{
+       int i, n = READ_ONCE(plane->nr_overlays);
+
+       smp_rmb();
+       for (i = 0; i < n; i++) {
+               if (READ_ONCE(plane->overlays[i].gfn) != gfn)
+                       continue;
+               /*
+                * The acquire pairs with the smp_store_release() updating
+                * an entry: a reader which observes the new page also
+                * observes the uaddr written before it.
+                */
+               if (smp_load_acquire(&plane->overlays[i].page))
+                       return READ_ONCE(plane->overlays[i].uaddr);
+               return 0;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_plane_overlay_uaddr);
+
+/*
+ * The invalidation window registered around an overlay update must
+ * cover every gfn whose in-flight fault could install a huge leaf
+ * spanning the overlaid gfn, i.e. the naturally-aligned region of the
+ * widest stage-2 huge mapping any plane-capable architecture supports
+ * (a 1GiB page on x86).  A narrower window lets a racing fault on a
+ * neighbouring gfn, which sampled nr_overlays and mmu_invalidate_seq
+ * before the update, pass mmu_invalidate_retry_gfn() and install a
+ * huge SPTE over the overlay after the zap.
+ */
+#define KVM_PLANE_OVERLAY_INVAL_PAGES  (SZ_1G >> PAGE_SHIFT)
+
+static void kvm_plane_overlay_invalidate_add(struct kvm *kvm, gfn_t gfn)
+{
+       gfn_t start = ALIGN_DOWN(gfn, KVM_PLANE_OVERLAY_INVAL_PAGES);
+
+       kvm_mmu_invalidate_range_add(kvm, start,
+                                    start + KVM_PLANE_OVERLAY_INVAL_PAGES);
+}
+
+/*
+ * Drop every translation of @gfn, in all address spaces and all planes'
+ * roots.  A huge leaf spanning the gfn intersects the single-page range
+ * and is zapped with it.  Hardware TLBs are not flushed here.
+ */
+static void kvm_plane_overlay_unmap(struct kvm *kvm, gfn_t gfn)
+{
+#ifdef CONFIG_KVM_GENERIC_MMU_NOTIFIER
+       struct kvm_memslot_iter iter;
+       struct kvm_memslots *slots;
+       int i;
+
+       KVM_MMU_LOCK(kvm);
+       for (i = 0; i < kvm_arch_nr_memslot_as_ids(kvm); i++) {
+               slots = __kvm_memslots(kvm, i);
+
+               kvm_for_each_memslot_in_gfn_range(&iter, slots, gfn, gfn + 1) {
+                       struct kvm_gfn_range range = {
+                               .slot = iter.slot,
+                               .start = gfn,
+                               .end = gfn + 1,
+                               .attr_filter = KVM_FILTER_PRIVATE |
+                                              KVM_FILTER_SHARED,
+                               .may_block = true,
+                       };
+
+                       kvm_unmap_gfn_range(kvm, &range);
+               }
+       }
+       KVM_MMU_UNLOCK(kvm);
+#else
+       kvm_arch_flush_shadow_all(kvm);
+#endif
+}
+
+static int kvm_vm_ioctl_set_plane_overlay(struct kvm *kvm,
+                                         struct kvm_plane_memory_overlay *ov)
+{
+       struct kvm_plane_overlay *e = NULL;
+       struct page *page = NULL, *old_page = NULL;
+       struct kvm_plane *plane;
+       bool moved = false;
+       gfn_t moved_gfn = 0;
+       int i, r;
+
+       if (ov->flags || ov->plane >= KVM_MAX_PLANES)
+               return -EINVAL;
+       if (ov->uaddr & ~PAGE_MASK)
+               return -EINVAL;
+       plane = kvm->planes[ov->plane];
+       if (!plane)
+               return -EINVAL;
+
+       if (ov->uaddr) {
+               r = pin_user_pages_fast(ov->uaddr, 1,
+                                       FOLL_WRITE | FOLL_LONGTERM, &page);
+               if (r != 1)
+                       return r < 0 ? r : -EFAULT;
+       }
+
+       mutex_lock(&kvm->slots_lock);
+       for (i = 0; i < plane->nr_overlays; i++) {
+               if (plane->overlays[i].gfn == ov->gfn) {
+                       e = &plane->overlays[i];
+                       break;
+               }
+       }
+
+       /*
+        * Removing an overlay which was never installed changes nothing;
+        * don't zap and flush for it.
+        */
+       if (!ov->uaddr && !e) {
+               mutex_unlock(&kvm->slots_lock);
+               return 0;
+       }
+
+       /*
+        * guest_memfd-backed and private gfns have their own backing
+        * contract which an ordinary pinned user page cannot substitute
+        * for, and kvm_mmu_faultin_pfn_gmem() does not consult overlays.
+        * Both the private attribute and the memslot flags are stable
+        * under slots_lock.
+        */
+       if (ov->uaddr) {
+               struct kvm_memory_slot *slot = gfn_to_memslot(kvm, ov->gfn);
+
+               if ((slot && kvm_memslot_is_gmem_only(slot)) ||
+                   kvm_mem_is_private(kvm, ov->gfn)) {
+                       mutex_unlock(&kvm->slots_lock);
+                       unpin_user_page(page);
+                       return -EINVAL;
+               }
+       }
+
+       if (ov->uaddr) {
+               if (!e) {
+                       if (plane->nr_overlays >= KVM_PLANE_MAX_OVERLAYS) {
+                               mutex_unlock(&kvm->slots_lock);
+                               unpin_user_page(page);
+                               return -ENOSPC;
+                       }
+                       e = &plane->overlays[plane->nr_overlays];
+                       e->gfn = ov->gfn;
+                       e->uaddr = ov->uaddr;
+                       e->page = page;
+                       /*
+                        * Publish the fully-initialised entry before
+                        * raising the count; pairs with the smp_rmb() in
+                        * the lockless readers.
+                        */
+                       smp_wmb();
+                       WRITE_ONCE(plane->nr_overlays, plane->nr_overlays + 1);
+               } else {
+                       old_page = e->page;
+                       WRITE_ONCE(e->uaddr, ov->uaddr);
+                       /*
+                        * Order the uaddr store before the page store; a
+                        * reader which acquires the new page must see the
+                        * uaddr it belongs to.
+                        */
+                       smp_store_release(&e->page, page);
+               }
+       } else if (e) {
+               struct kvm_plane_overlay *tail =
+                       &plane->overlays[plane->nr_overlays - 1];
+
+               old_page = e->page;
+               WRITE_ONCE(e->page, NULL);
+               WRITE_ONCE(e->uaddr, 0);
+
+               /*
+                * Reclaim the slot: move the tail entry into the vacated
+                * one and shrink the array.  A lockless reader scanning
+                * with the old count can miss the moved entry entirely
+                * (it may already have passed the vacated slot), so the
+                * moved gfn is added to the invalidation window below and
+                * any such in-flight fault retries.  The cleared tail is
+                * harmless to a stale-count reader: the live copy sits at
+                * a lower index and is found first.
+                */
+               if (e != tail) {
+                       moved = true;
+                       moved_gfn = tail->gfn;
+                       WRITE_ONCE(e->gfn, tail->gfn);
+                       WRITE_ONCE(e->uaddr, tail->uaddr);
+                       /* pairs with smp_load_acquire() in the readers */
+                       smp_store_release(&e->page, tail->page);
+               }
+               WRITE_ONCE(plane->nr_overlays, plane->nr_overlays - 1);
+               WRITE_ONCE(tail->page, NULL);
+               WRITE_ONCE(tail->uaddr, 0);
+               WRITE_ONCE(tail->gfn, 0);
+       }
+       /*
+        * Bump the invalidation sequence across the update and zap: a
+        * concurrent fault resolves the overlay pfn locklessly before
+        * taking mmu_lock, and without a sequence change
+        * is_page_fault_stale() would let it install an SPTE for the OLD
+        * backing after the zap, mapping a page this function is about
+        * to unpin.
+        */
+       KVM_MMU_LOCK(kvm);
+       kvm_mmu_invalidate_begin(kvm);
+       kvm_plane_overlay_invalidate_add(kvm, ov->gfn);
+       if (moved)
+               kvm_plane_overlay_invalidate_add(kvm, moved_gfn);
+       KVM_MMU_UNLOCK(kvm);
+
+       mutex_unlock(&kvm->slots_lock);
+
+       /*
+        * Drop any translation mapping the old or new backing.  The unmap
+        * removes the SPTEs but does not flush hardware TLBs: a vCPU that
+        * mapped the gfn before the update still holds a translation to
+        * the old backing in its TLB (tagged by the live ASID on SVM,
+        * which nothing else invalidates).  Flush before the old page is
+        * unpinned: KVM_REQ_TLB_FLUSH is a per-plane request, so the
+        * fan-out in kvm_make_vcpu_request() reaches every plane twin and
+        * each flushes its own hardware context.
+        */
+       kvm_plane_overlay_unmap(kvm, ov->gfn);
+       if (moved)
+               kvm_plane_overlay_unmap(kvm, moved_gfn);
+       kvm_flush_remote_tlbs(kvm);
+
+       KVM_MMU_LOCK(kvm);
+       kvm_mmu_invalidate_end(kvm);
+       KVM_MMU_UNLOCK(kvm);
+
+       if (old_page)
+               unpin_user_pages_dirty_lock(&old_page, 1, true);
+       return 0;
+}
+
 static long kvm_vm_ioctl(struct file *filp,
                           unsigned int ioctl, unsigned long arg)
 {
@@ -5906,6 +6168,15 @@ static long kvm_vm_ioctl(struct file *filp,
        if (kvm->mm != current->mm || kvm->vm_dead)
                return -EIO;
        switch (ioctl) {
+       case KVM_SET_PLANE_MEMORY_OVERLAY: {
+               struct kvm_plane_memory_overlay ov;
+
+               r = -EFAULT;
+               if (copy_from_user(&ov, argp, sizeof(ov)))
+                       goto out;
+               r = kvm_vm_ioctl_set_plane_overlay(kvm, &ov);
+               break;
+       }
        case KVM_CREATE_PLANE:
                r = kvm_vm_ioctl_create_plane(kvm, arg);
                break;
-- 
2.50.1


Reply via email to