]> Git Repo - linux.git/commitdiff
Merge tag 'x86_sev_for_v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
authorLinus Torvalds <[email protected]>
Tue, 27 Jun 2023 20:26:30 +0000 (13:26 -0700)
committerLinus Torvalds <[email protected]>
Tue, 27 Jun 2023 20:26:30 +0000 (13:26 -0700)
Pull x86 SEV updates from Borislav Petkov:

 - Some SEV and CC platform helpers cleanup and simplifications now that
   the usage patterns are becoming apparent

[ I'm sure I'm the only one that has gets confused by all the TLAs, but
  in case there are others: here SEV is AMD's "Secure Encrypted
  Virtualization" and CC is generic "Confidential Computing".

  There's also Intel SGX (Software Guard Extensions) and TDX (Trust
  Domain Extensions), along with all the vendor memory encryption
  extensions (SME, TSME, TME, and WTF).

  And then we have arm64 with RMA and CCA, and I probably forgot another
  dozen or so related acronyms    - Linus ]

* tag 'x86_sev_for_v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/coco: Get rid of accessor functions
  x86/sev: Get rid of special sev_es_enable_key
  x86/coco: Mark cc_platform_has() and descendants noinstr

1  2 
arch/x86/coco/tdx/tdx.c
arch/x86/hyperv/ivm.c
arch/x86/include/asm/sev.h
arch/x86/kernel/sev.c

diff --combined arch/x86/coco/tdx/tdx.c
index 0e2a16e7d6857a2c13df06a85d9dfee02d2ab431,971c6cfb15dd08525acddba7e19b11a5d3abd909..1d6b863c42b001c8c28c8bc36bc94b318c9df0d7
  #include <asm/insn-eval.h>
  #include <asm/pgtable.h>
  
 -/* TDX module Call Leaf IDs */
 -#define TDX_GET_INFO                  1
 -#define TDX_GET_VEINFO                        3
 -#define TDX_GET_REPORT                        4
 -#define TDX_ACCEPT_PAGE                       6
 -#define TDX_WR                                8
 -
 -/* TDCS fields. To be used by TDG.VM.WR and TDG.VM.RD module calls */
 -#define TDCS_NOTIFY_ENABLES           0x9100000000000010
 -
 -/* TDX hypercall Leaf IDs */
 -#define TDVMCALL_MAP_GPA              0x10001
 -#define TDVMCALL_REPORT_FATAL_ERROR   0x10003
 -
  /* MMIO direction */
  #define EPT_READ      0
  #define EPT_WRITE     1
  
  #define TDREPORT_SUBTYPE_0    0
  
 -/*
 - * Wrapper for standard use of __tdx_hypercall with no output aside from
 - * return code.
 - */
 -static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15)
 -{
 -      struct tdx_hypercall_args args = {
 -              .r10 = TDX_HYPERCALL_STANDARD,
 -              .r11 = fn,
 -              .r12 = r12,
 -              .r13 = r13,
 -              .r14 = r14,
 -              .r15 = r15,
 -      };
 -
 -      return __tdx_hypercall(&args);
 -}
 -
  /* Called from __tdx_hypercall() for unrecoverable failure */
  noinstr void __tdx_hypercall_failed(void)
  {
        panic("TDVMCALL failed. TDX module bug?");
  }
  
 -/*
 - * The TDG.VP.VMCALL-Instruction-execution sub-functions are defined
 - * independently from but are currently matched 1:1 with VMX EXIT_REASONs.
 - * Reusing the KVM EXIT_REASON macros makes it easier to connect the host and
 - * guest sides of these calls.
 - */
 -static __always_inline u64 hcall_func(u64 exit_reason)
 -{
 -      return exit_reason;
 -}
 -
  #ifdef CONFIG_KVM_GUEST
  long tdx_kvm_hypercall(unsigned int nr, unsigned long p1, unsigned long p2,
                       unsigned long p3, unsigned long p4)
@@@ -702,6 -745,47 +702,6 @@@ static bool tdx_cache_flush_required(vo
        return true;
  }
  
 -static bool try_accept_one(phys_addr_t *start, unsigned long len,
 -                        enum pg_level pg_level)
 -{
 -      unsigned long accept_size = page_level_size(pg_level);
 -      u64 tdcall_rcx;
 -      u8 page_size;
 -
 -      if (!IS_ALIGNED(*start, accept_size))
 -              return false;
 -
 -      if (len < accept_size)
 -              return false;
 -
 -      /*
 -       * Pass the page physical address to the TDX module to accept the
 -       * pending, private page.
 -       *
 -       * Bits 2:0 of RCX encode page size: 0 - 4K, 1 - 2M, 2 - 1G.
 -       */
 -      switch (pg_level) {
 -      case PG_LEVEL_4K:
 -              page_size = 0;
 -              break;
 -      case PG_LEVEL_2M:
 -              page_size = 1;
 -              break;
 -      case PG_LEVEL_1G:
 -              page_size = 2;
 -              break;
 -      default:
 -              return false;
 -      }
 -
 -      tdcall_rcx = *start | page_size;
 -      if (__tdx_module_call(TDX_ACCEPT_PAGE, tdcall_rcx, 0, 0, 0, NULL))
 -              return false;
 -
 -      *start += accept_size;
 -      return true;
 -}
 -
  /*
   * Inform the VMM of the guest's intent for this physical page: shared with
   * the VMM or private to the guest.  The VMM is expected to change its mapping
@@@ -726,34 -810,33 +726,34 @@@ static bool tdx_enc_status_changed(unsi
        if (_tdx_hypercall(TDVMCALL_MAP_GPA, start, end - start, 0, 0))
                return false;
  
 -      /* private->shared conversion  requires only MapGPA call */
 -      if (!enc)
 -              return true;
 +      /* shared->private conversion requires memory to be accepted before use */
 +      if (enc)
 +              return tdx_accept_memory(start, end);
 +
 +      return true;
 +}
  
 +static bool tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,
 +                                        bool enc)
 +{
        /*
 -       * For shared->private conversion, accept the page using
 -       * TDX_ACCEPT_PAGE TDX module call.
 +       * Only handle shared->private conversion here.
 +       * See the comment in tdx_early_init().
         */
 -      while (start < end) {
 -              unsigned long len = end - start;
 -
 -              /*
 -               * Try larger accepts first. It gives chance to VMM to keep
 -               * 1G/2M SEPT entries where possible and speeds up process by
 -               * cutting number of hypercalls (if successful).
 -               */
 -
 -              if (try_accept_one(&start, len, PG_LEVEL_1G))
 -                      continue;
 -
 -              if (try_accept_one(&start, len, PG_LEVEL_2M))
 -                      continue;
 -
 -              if (!try_accept_one(&start, len, PG_LEVEL_4K))
 -                      return false;
 -      }
 +      if (enc)
 +              return tdx_enc_status_changed(vaddr, numpages, enc);
 +      return true;
 +}
  
 +static bool tdx_enc_status_change_finish(unsigned long vaddr, int numpages,
 +                                       bool enc)
 +{
 +      /*
 +       * Only handle private->shared conversion here.
 +       * See the comment in tdx_early_init().
 +       */
 +      if (!enc)
 +              return tdx_enc_status_changed(vaddr, numpages, enc);
        return true;
  }
  
@@@ -769,7 -852,7 +769,7 @@@ void __init tdx_early_init(void
  
        setup_force_cpu_cap(X86_FEATURE_TDX_GUEST);
  
-       cc_set_vendor(CC_VENDOR_INTEL);
+       cc_vendor = CC_VENDOR_INTEL;
        tdx_parse_tdinfo(&cc_mask);
        cc_set_mask(cc_mask);
  
         */
        physical_mask &= cc_mask - 1;
  
 -      x86_platform.guest.enc_cache_flush_required = tdx_cache_flush_required;
 -      x86_platform.guest.enc_tlb_flush_required   = tdx_tlb_flush_required;
 -      x86_platform.guest.enc_status_change_finish = tdx_enc_status_changed;
 +      /*
 +       * The kernel mapping should match the TDX metadata for the page.
 +       * load_unaligned_zeropad() can touch memory *adjacent* to that which is
 +       * owned by the caller and can catch even _momentary_ mismatches.  Bad
 +       * things happen on mismatch:
 +       *
 +       *   - Private mapping => Shared Page  == Guest shutdown
 +         *   - Shared mapping  => Private Page == Recoverable #VE
 +       *
 +       * guest.enc_status_change_prepare() converts the page from
 +       * shared=>private before the mapping becomes private.
 +       *
 +       * guest.enc_status_change_finish() converts the page from
 +       * private=>shared after the mapping becomes private.
 +       *
 +       * In both cases there is a temporary shared mapping to a private page,
 +       * which can result in a #VE.  But, there is never a private mapping to
 +       * a shared page.
 +       */
 +      x86_platform.guest.enc_status_change_prepare = tdx_enc_status_change_prepare;
 +      x86_platform.guest.enc_status_change_finish  = tdx_enc_status_change_finish;
 +
 +      x86_platform.guest.enc_cache_flush_required  = tdx_cache_flush_required;
 +      x86_platform.guest.enc_tlb_flush_required    = tdx_tlb_flush_required;
 +
 +      /*
 +       * TDX intercepts the RDMSR to read the X2APIC ID in the parallel
 +       * bringup low level code. That raises #VE which cannot be handled
 +       * there.
 +       *
 +       * Intel-TDX has a secure RDMSR hypercall, but that needs to be
 +       * implemented seperately in the low level startup ASM code.
 +       * Until that is in place, disable parallel bringup for TDX.
 +       */
 +      x86_cpuinit.parallel_bringup = false;
  
        pr_info("Guest detected\n");
  }
diff --combined arch/x86/hyperv/ivm.c
index 6f7c1b5606ad4d5ff643db8a695ddf4933736818,868f5dea2e10caa424ab47b2bf86794770c05a2b..14f46ad2ca644dc92ec1501d2e5d26d1e2a9697f
@@@ -17,7 -17,6 +17,7 @@@
  #include <asm/mem_encrypt.h>
  #include <asm/mshyperv.h>
  #include <asm/hypervisor.h>
 +#include <asm/mtrr.h>
  
  #ifdef CONFIG_AMD_MEM_ENCRYPT
  
@@@ -365,7 -364,7 +365,7 @@@ void __init hv_vtom_init(void
         * Set it here to indicate a vTOM VM.
         */
        sev_status = MSR_AMD64_SNP_VTOM;
-       cc_set_vendor(CC_VENDOR_AMD);
+       cc_vendor = CC_VENDOR_AMD;
        cc_set_mask(ms_hyperv.shared_gpa_boundary);
        physical_mask &= ms_hyperv.shared_gpa_boundary - 1;
  
        x86_platform.guest.enc_cache_flush_required = hv_vtom_cache_flush_required;
        x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
        x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
 +
 +      /* Set WB as the default cache mode. */
 +      mtrr_overwrite_state(NULL, 0, MTRR_TYPE_WRBACK);
  }
  
  #endif /* CONFIG_AMD_MEM_ENCRYPT */
index 86e1296e87f513b72b57b3ea91668fdf81bb6dfe,f14060cbead28b310780b63b08510dbc4d118d88..66c806784c5256bd372aa029a364bcdf24e98e84
@@@ -14,6 -14,7 +14,7 @@@
  #include <asm/insn.h>
  #include <asm/sev-common.h>
  #include <asm/bootparam.h>
+ #include <asm/coco.h>
  
  #define GHCB_PROTOCOL_MIN     1ULL
  #define GHCB_PROTOCOL_MAX     2ULL
@@@ -80,15 -81,11 +81,15 @@@ extern void vc_no_ghcb(void)
  extern void vc_boot_ghcb(void);
  extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
  
 +/* PVALIDATE return codes */
 +#define PVALIDATE_FAIL_SIZEMISMATCH   6
 +
  /* Software defined (when rFlags.CF = 1) */
  #define PVALIDATE_FAIL_NOUPDATE               255
  
  /* RMP page size */
  #define RMP_PG_SIZE_4K                        0
 +#define RMP_PG_SIZE_2M                        1
  
  #define RMPADJUST_VMSA_PAGE_BIT               BIT(16)
  
@@@ -140,24 -137,26 +141,26 @@@ struct snp_secrets_page_layout 
  } __packed;
  
  #ifdef CONFIG_AMD_MEM_ENCRYPT
- extern struct static_key_false sev_es_enable_key;
  extern void __sev_es_ist_enter(struct pt_regs *regs);
  extern void __sev_es_ist_exit(void);
  static __always_inline void sev_es_ist_enter(struct pt_regs *regs)
  {
-       if (static_branch_unlikely(&sev_es_enable_key))
+       if (cc_vendor == CC_VENDOR_AMD &&
+           cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
                __sev_es_ist_enter(regs);
  }
  static __always_inline void sev_es_ist_exit(void)
  {
-       if (static_branch_unlikely(&sev_es_enable_key))
+       if (cc_vendor == CC_VENDOR_AMD &&
+           cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
                __sev_es_ist_exit();
  }
  extern int sev_es_setup_ap_jump_table(struct real_mode_header *rmh);
  extern void __sev_es_nmi_complete(void);
  static __always_inline void sev_es_nmi_complete(void)
  {
-       if (static_branch_unlikely(&sev_es_enable_key))
+       if (cc_vendor == CC_VENDOR_AMD &&
+           cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT))
                __sev_es_nmi_complete();
  }
  extern int __init sev_es_efi_map_ghcbs(pgd_t *pgd);
@@@ -196,17 -195,16 +199,17 @@@ struct snp_guest_request_ioctl
  
  void setup_ghcb(void);
  void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
 -                                       unsigned int npages);
 +                                       unsigned long npages);
  void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr,
 -                                      unsigned int npages);
 +                                      unsigned long npages);
  void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op);
 -void snp_set_memory_shared(unsigned long vaddr, unsigned int npages);
 -void snp_set_memory_private(unsigned long vaddr, unsigned int npages);
 +void snp_set_memory_shared(unsigned long vaddr, unsigned long npages);
 +void snp_set_memory_private(unsigned long vaddr, unsigned long npages);
  void snp_set_wakeup_secondary_cpu(void);
  bool snp_init(struct boot_params *bp);
  void __init __noreturn snp_abort(void);
  int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
 +void snp_accept_memory(phys_addr_t start, phys_addr_t end);
  #else
  static inline void sev_es_ist_enter(struct pt_regs *regs) { }
  static inline void sev_es_ist_exit(void) { }
@@@ -217,12 -215,12 +220,12 @@@ static inline int pvalidate(unsigned lo
  static inline int rmpadjust(unsigned long vaddr, bool rmp_psize, unsigned long attrs) { return 0; }
  static inline void setup_ghcb(void) { }
  static inline void __init
 -early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned int npages) { }
 +early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, unsigned long npages) { }
  static inline void __init
 -early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, unsigned int npages) { }
 +early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, unsigned long npages) { }
  static inline void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op) { }
 -static inline void snp_set_memory_shared(unsigned long vaddr, unsigned int npages) { }
 -static inline void snp_set_memory_private(unsigned long vaddr, unsigned int npages) { }
 +static inline void snp_set_memory_shared(unsigned long vaddr, unsigned long npages) { }
 +static inline void snp_set_memory_private(unsigned long vaddr, unsigned long npages) { }
  static inline void snp_set_wakeup_secondary_cpu(void) { }
  static inline bool snp_init(struct boot_params *bp) { return false; }
  static inline void snp_abort(void) { }
@@@ -230,8 -228,6 +233,8 @@@ static inline int snp_issue_guest_reque
  {
        return -ENOTTY;
  }
 +
 +static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
  #endif
  
  #endif
diff --combined arch/x86/kernel/sev.c
index a0af3908ed49e441a94d3897e15692582bf90d7d,0ec0d963b8b70e68e0092a30328e84e87a5d6727..1ee7bed453ded5df0d50469b6f75fbd7ca1ed189
@@@ -113,25 -113,11 +113,23 @@@ struct ghcb_state 
  };
  
  static DEFINE_PER_CPU(struct sev_es_runtime_data*, runtime_data);
- DEFINE_STATIC_KEY_FALSE(sev_es_enable_key);
  static DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa);
  
  struct sev_config {
        __u64 debug             : 1,
 -            __reserved        : 63;
 +
 +            /*
 +             * A flag used by __set_pages_state() that indicates when the
 +             * per-CPU GHCB has been created and registered and thus can be
 +             * used by the BSP instead of the early boot GHCB.
 +             *
 +             * For APs, the per-CPU GHCB is created before they are started
 +             * and registered upon startup, so this flag can be used globally
 +             * for the BSP and APs.
 +             */
 +            ghcbs_initialized : 1,
 +
 +            __reserved        : 62;
  };
  
  static struct sev_config sev_cfg __read_mostly;
@@@ -657,26 -643,32 +655,26 @@@ static u64 __init get_jump_table_addr(v
        return ret;
  }
  
 -static void pvalidate_pages(unsigned long vaddr, unsigned int npages, bool validate)
 -{
 -      unsigned long vaddr_end;
 -      int rc;
 -
 -      vaddr = vaddr & PAGE_MASK;
 -      vaddr_end = vaddr + (npages << PAGE_SHIFT);
 -
 -      while (vaddr < vaddr_end) {
 -              rc = pvalidate(vaddr, RMP_PG_SIZE_4K, validate);
 -              if (WARN(rc, "Failed to validate address 0x%lx ret %d", vaddr, rc))
 -                      sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
 -
 -              vaddr = vaddr + PAGE_SIZE;
 -      }
 -}
 -
 -static void __init early_set_pages_state(unsigned long paddr, unsigned int npages, enum psc_op op)
 +static void early_set_pages_state(unsigned long vaddr, unsigned long paddr,
 +                                unsigned long npages, enum psc_op op)
  {
        unsigned long paddr_end;
        u64 val;
 +      int ret;
 +
 +      vaddr = vaddr & PAGE_MASK;
  
        paddr = paddr & PAGE_MASK;
        paddr_end = paddr + (npages << PAGE_SHIFT);
  
        while (paddr < paddr_end) {
 +              if (op == SNP_PAGE_STATE_SHARED) {
 +                      /* Page validation must be rescinded before changing to shared */
 +                      ret = pvalidate(vaddr, RMP_PG_SIZE_4K, false);
 +                      if (WARN(ret, "Failed to validate address 0x%lx ret %d", paddr, ret))
 +                              goto e_term;
 +              }
 +
                /*
                 * Use the MSR protocol because this function can be called before
                 * the GHCB is established.
                         paddr, GHCB_MSR_PSC_RESP_VAL(val)))
                        goto e_term;
  
 -              paddr = paddr + PAGE_SIZE;
 +              if (op == SNP_PAGE_STATE_PRIVATE) {
 +                      /* Page validation must be performed after changing to private */
 +                      ret = pvalidate(vaddr, RMP_PG_SIZE_4K, true);
 +                      if (WARN(ret, "Failed to validate address 0x%lx ret %d", paddr, ret))
 +                              goto e_term;
 +              }
 +
 +              vaddr += PAGE_SIZE;
 +              paddr += PAGE_SIZE;
        }
  
        return;
@@@ -715,7 -699,7 +713,7 @@@ e_term
  }
  
  void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
 -                                       unsigned int npages)
 +                                       unsigned long npages)
  {
        /*
         * This can be invoked in early boot while running identity mapped, so
          * Ask the hypervisor to mark the memory pages as private in the RMP
          * table.
          */
 -      early_set_pages_state(paddr, npages, SNP_PAGE_STATE_PRIVATE);
 -
 -      /* Validate the memory pages after they've been added in the RMP table. */
 -      pvalidate_pages(vaddr, npages, true);
 +      early_set_pages_state(vaddr, paddr, npages, SNP_PAGE_STATE_PRIVATE);
  }
  
  void __init early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr,
 -                                      unsigned int npages)
 +                                      unsigned long npages)
  {
        /*
         * This can be invoked in early boot while running identity mapped, so
        if (!(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
                return;
  
 -      /* Invalidate the memory pages before they are marked shared in the RMP table. */
 -      pvalidate_pages(vaddr, npages, false);
 -
         /* Ask hypervisor to mark the memory pages shared in the RMP table. */
 -      early_set_pages_state(paddr, npages, SNP_PAGE_STATE_SHARED);
 +      early_set_pages_state(vaddr, paddr, npages, SNP_PAGE_STATE_SHARED);
  }
  
  void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op)
                WARN(1, "invalid memory op %d\n", op);
  }
  
 -static int vmgexit_psc(struct snp_psc_desc *desc)
 +static unsigned long __set_pages_state(struct snp_psc_desc *data, unsigned long vaddr,
 +                                     unsigned long vaddr_end, int op)
  {
 -      int cur_entry, end_entry, ret = 0;
 -      struct snp_psc_desc *data;
        struct ghcb_state state;
 -      struct es_em_ctxt ctxt;
 -      unsigned long flags;
 -      struct ghcb *ghcb;
 -
 -      /*
 -       * __sev_get_ghcb() needs to run with IRQs disabled because it is using
 -       * a per-CPU GHCB.
 -       */
 -      local_irq_save(flags);
 -
 -      ghcb = __sev_get_ghcb(&state);
 -      if (!ghcb) {
 -              ret = 1;
 -              goto out_unlock;
 -      }
 -
 -      /* Copy the input desc into GHCB shared buffer */
 -      data = (struct snp_psc_desc *)ghcb->shared_buffer;
 -      memcpy(ghcb->shared_buffer, desc, min_t(int, GHCB_SHARED_BUF_SIZE, sizeof(*desc)));
 -
 -      /*
 -       * As per the GHCB specification, the hypervisor can resume the guest
 -       * before processing all the entries. Check whether all the entries
 -       * are processed. If not, then keep retrying. Note, the hypervisor
 -       * will update the data memory directly to indicate the status, so
 -       * reference the data->hdr everywhere.
 -       *
 -       * The strategy here is to wait for the hypervisor to change the page
 -       * state in the RMP table before guest accesses the memory pages. If the
 -       * page state change was not successful, then later memory access will
 -       * result in a crash.
 -       */
 -      cur_entry = data->hdr.cur_entry;
 -      end_entry = data->hdr.end_entry;
 -
 -      while (data->hdr.cur_entry <= data->hdr.end_entry) {
 -              ghcb_set_sw_scratch(ghcb, (u64)__pa(data));
 -
 -              /* This will advance the shared buffer data points to. */
 -              ret = sev_es_ghcb_hv_call(ghcb, &ctxt, SVM_VMGEXIT_PSC, 0, 0);
 -
 -              /*
 -               * Page State Change VMGEXIT can pass error code through
 -               * exit_info_2.
 -               */
 -              if (WARN(ret || ghcb->save.sw_exit_info_2,
 -                       "SNP: PSC failed ret=%d exit_info_2=%llx\n",
 -                       ret, ghcb->save.sw_exit_info_2)) {
 -                      ret = 1;
 -                      goto out;
 -              }
 -
 -              /* Verify that reserved bit is not set */
 -              if (WARN(data->hdr.reserved, "Reserved bit is set in the PSC header\n")) {
 -                      ret = 1;
 -                      goto out;
 -              }
 -
 -              /*
 -               * Sanity check that entry processing is not going backwards.
 -               * This will happen only if hypervisor is tricking us.
 -               */
 -              if (WARN(data->hdr.end_entry > end_entry || cur_entry > data->hdr.cur_entry,
 -"SNP: PSC processing going backward, end_entry %d (got %d) cur_entry %d (got %d)\n",
 -                       end_entry, data->hdr.end_entry, cur_entry, data->hdr.cur_entry)) {
 -                      ret = 1;
 -                      goto out;
 -              }
 -      }
 -
 -out:
 -      __sev_put_ghcb(&state);
 -
 -out_unlock:
 -      local_irq_restore(flags);
 -
 -      return ret;
 -}
 -
 -static void __set_pages_state(struct snp_psc_desc *data, unsigned long vaddr,
 -                            unsigned long vaddr_end, int op)
 -{
 +      bool use_large_entry;
        struct psc_hdr *hdr;
        struct psc_entry *e;
 +      unsigned long flags;
        unsigned long pfn;
 +      struct ghcb *ghcb;
        int i;
  
        hdr = &data->hdr;
        memset(data, 0, sizeof(*data));
        i = 0;
  
 -      while (vaddr < vaddr_end) {
 -              if (is_vmalloc_addr((void *)vaddr))
 +      while (vaddr < vaddr_end && i < ARRAY_SIZE(data->entries)) {
 +              hdr->end_entry = i;
 +
 +              if (is_vmalloc_addr((void *)vaddr)) {
                        pfn = vmalloc_to_pfn((void *)vaddr);
 -              else
 +                      use_large_entry = false;
 +              } else {
                        pfn = __pa(vaddr) >> PAGE_SHIFT;
 +                      use_large_entry = true;
 +              }
  
                e->gfn = pfn;
                e->operation = op;
 -              hdr->end_entry = i;
  
 -              /*
 -               * Current SNP implementation doesn't keep track of the RMP page
 -               * size so use 4K for simplicity.
 -               */
 -              e->pagesize = RMP_PG_SIZE_4K;
 +              if (use_large_entry && IS_ALIGNED(vaddr, PMD_SIZE) &&
 +                  (vaddr_end - vaddr) >= PMD_SIZE) {
 +                      e->pagesize = RMP_PG_SIZE_2M;
 +                      vaddr += PMD_SIZE;
 +              } else {
 +                      e->pagesize = RMP_PG_SIZE_4K;
 +                      vaddr += PAGE_SIZE;
 +              }
  
 -              vaddr = vaddr + PAGE_SIZE;
                e++;
                i++;
        }
  
 -      if (vmgexit_psc(data))
 +      /* Page validation must be rescinded before changing to shared */
 +      if (op == SNP_PAGE_STATE_SHARED)
 +              pvalidate_pages(data);
 +
 +      local_irq_save(flags);
 +
 +      if (sev_cfg.ghcbs_initialized)
 +              ghcb = __sev_get_ghcb(&state);
 +      else
 +              ghcb = boot_ghcb;
 +
 +      /* Invoke the hypervisor to perform the page state changes */
 +      if (!ghcb || vmgexit_psc(ghcb, data))
                sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
 +
 +      if (sev_cfg.ghcbs_initialized)
 +              __sev_put_ghcb(&state);
 +
 +      local_irq_restore(flags);
 +
 +      /* Page validation must be performed after changing to private */
 +      if (op == SNP_PAGE_STATE_PRIVATE)
 +              pvalidate_pages(data);
 +
 +      return vaddr;
  }
  
 -static void set_pages_state(unsigned long vaddr, unsigned int npages, int op)
 +static void set_pages_state(unsigned long vaddr, unsigned long npages, int op)
  {
 -      unsigned long vaddr_end, next_vaddr;
 -      struct snp_psc_desc *desc;
 +      struct snp_psc_desc desc;
 +      unsigned long vaddr_end;
  
 -      desc = kmalloc(sizeof(*desc), GFP_KERNEL_ACCOUNT);
 -      if (!desc)
 -              panic("SNP: failed to allocate memory for PSC descriptor\n");
 +      /* Use the MSR protocol when a GHCB is not available. */
 +      if (!boot_ghcb)
 +              return early_set_pages_state(vaddr, __pa(vaddr), npages, op);
  
        vaddr = vaddr & PAGE_MASK;
        vaddr_end = vaddr + (npages << PAGE_SHIFT);
  
 -      while (vaddr < vaddr_end) {
 -              /* Calculate the last vaddr that fits in one struct snp_psc_desc. */
 -              next_vaddr = min_t(unsigned long, vaddr_end,
 -                                 (VMGEXIT_PSC_MAX_ENTRY * PAGE_SIZE) + vaddr);
 -
 -              __set_pages_state(desc, vaddr, next_vaddr, op);
 -
 -              vaddr = next_vaddr;
 -      }
 -
 -      kfree(desc);
 +      while (vaddr < vaddr_end)
 +              vaddr = __set_pages_state(&desc, vaddr, vaddr_end, op);
  }
  
 -void snp_set_memory_shared(unsigned long vaddr, unsigned int npages)
 +void snp_set_memory_shared(unsigned long vaddr, unsigned long npages)
  {
        if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
                return;
  
 -      pvalidate_pages(vaddr, npages, false);
 -
        set_pages_state(vaddr, npages, SNP_PAGE_STATE_SHARED);
  }
  
 -void snp_set_memory_private(unsigned long vaddr, unsigned int npages)
 +void snp_set_memory_private(unsigned long vaddr, unsigned long npages)
  {
        if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
                return;
  
        set_pages_state(vaddr, npages, SNP_PAGE_STATE_PRIVATE);
 +}
  
 -      pvalidate_pages(vaddr, npages, true);
 +void snp_accept_memory(phys_addr_t start, phys_addr_t end)
 +{
 +      unsigned long vaddr;
 +      unsigned int npages;
 +
 +      if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
 +              return;
 +
 +      vaddr = (unsigned long)__va(start);
 +      npages = (end - start) >> PAGE_SHIFT;
 +
 +      set_pages_state(vaddr, npages, SNP_PAGE_STATE_PRIVATE);
  }
  
  static int snp_set_vmsa(void *va, bool vmsa)
@@@ -1225,8 -1265,6 +1223,8 @@@ void setup_ghcb(void
                if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
                        snp_register_per_cpu_ghcb();
  
 +              sev_cfg.ghcbs_initialized = true;
 +
                return;
        }
  
@@@ -1288,7 -1326,7 +1286,7 @@@ static void sev_es_play_dead(void
         * If we get here, the VCPU was woken up again. Jump to CPU
         * startup code to get it back online.
         */
 -      start_cpu0();
 +      soft_restart_cpu();
  }
  #else  /* CONFIG_HOTPLUG_CPU */
  #define sev_es_play_dead      native_play_dead
@@@ -1355,9 -1393,6 +1353,6 @@@ void __init sev_es_init_vc_handling(voi
                        sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
        }
  
-       /* Enable SEV-ES special handling */
-       static_branch_enable(&sev_es_enable_key);
        /* Initialize per-cpu GHCB pages */
        for_each_possible_cpu(cpu) {
                alloc_runtime_data(cpu);
This page took 0.128679 seconds and 4 git commands to generate.