Merge branch 'kvm-arm64/nvhe-panic-info' into kvmarm-master/next
authorMarc Zyngier <maz@kernel.org>
Tue, 13 Apr 2021 14:38:03 +0000 (15:38 +0100)
committerMarc Zyngier <maz@kernel.org>
Tue, 13 Apr 2021 14:38:03 +0000 (15:38 +0100)
Signed-off-by: Marc Zyngier <maz@kernel.org>
1  2 
arch/arm64/include/asm/kvm_hyp.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/kernel/image-vars.h
arch/arm64/kvm/hyp/include/hyp/switch.h
arch/arm64/kvm/hyp/include/nvhe/gfp.h
arch/arm64/kvm/hyp/include/nvhe/memory.h
arch/arm64/kvm/hyp/nvhe/hyp-main.c
arch/arm64/kvm/hyp/nvhe/mem_protect.c

Simple merge
Simple merge
Simple merge
index 55b3f0c,0000000..18a4494
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,68 @@@
-               hyp_panic();
 +/* SPDX-License-Identifier: GPL-2.0-only */
 +#ifndef __KVM_HYP_GFP_H
 +#define __KVM_HYP_GFP_H
 +
 +#include <linux/list.h>
 +
 +#include <nvhe/memory.h>
 +#include <nvhe/spinlock.h>
 +
 +#define HYP_NO_ORDER  UINT_MAX
 +
 +struct hyp_pool {
 +      /*
 +       * Spinlock protecting concurrent changes to the memory pool as well as
 +       * the struct hyp_page of the pool's pages until we have a proper atomic
 +       * API at EL2.
 +       */
 +      hyp_spinlock_t lock;
 +      struct list_head free_area[MAX_ORDER];
 +      phys_addr_t range_start;
 +      phys_addr_t range_end;
 +      unsigned int max_order;
 +};
 +
 +static inline void hyp_page_ref_inc(struct hyp_page *p)
 +{
 +      struct hyp_pool *pool = hyp_page_to_pool(p);
 +
 +      hyp_spin_lock(&pool->lock);
 +      p->refcount++;
 +      hyp_spin_unlock(&pool->lock);
 +}
 +
 +static inline int hyp_page_ref_dec_and_test(struct hyp_page *p)
 +{
 +      struct hyp_pool *pool = hyp_page_to_pool(p);
 +      int ret;
 +
 +      hyp_spin_lock(&pool->lock);
 +      p->refcount--;
 +      ret = (p->refcount == 0);
 +      hyp_spin_unlock(&pool->lock);
 +
 +      return ret;
 +}
 +
 +static inline void hyp_set_page_refcounted(struct hyp_page *p)
 +{
 +      struct hyp_pool *pool = hyp_page_to_pool(p);
 +
 +      hyp_spin_lock(&pool->lock);
 +      if (p->refcount) {
 +              hyp_spin_unlock(&pool->lock);
++              BUG();
 +      }
 +      p->refcount = 1;
 +      hyp_spin_unlock(&pool->lock);
 +}
 +
 +/* Allocation */
 +void *hyp_alloc_pages(struct hyp_pool *pool, unsigned int order);
 +void hyp_get_page(void *addr);
 +void hyp_put_page(void *addr);
 +
 +/* Used pages cannot be freed */
 +int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages,
 +                unsigned int reserved_pages);
 +#endif /* __KVM_HYP_GFP_H */
index d2fb307,0000000..fd78bde
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,51 @@@
- extern s64 hyp_physvirt_offset;
 +/* SPDX-License-Identifier: GPL-2.0-only */
 +#ifndef __KVM_HYP_MEMORY_H
 +#define __KVM_HYP_MEMORY_H
 +
++#include <asm/kvm_mmu.h>
 +#include <asm/page.h>
 +
 +#include <linux/types.h>
 +
 +struct hyp_pool;
 +struct hyp_page {
 +      unsigned int refcount;
 +      unsigned int order;
 +      struct hyp_pool *pool;
 +      struct list_head node;
 +};
 +
- #define __hyp_pa(virt)        ((phys_addr_t)(virt) + hyp_physvirt_offset)
 +extern u64 __hyp_vmemmap;
 +#define hyp_vmemmap ((struct hyp_page *)__hyp_vmemmap)
 +
 +#define __hyp_va(phys)        ((void *)((phys_addr_t)(phys) - hyp_physvirt_offset))
 +
 +static inline void *hyp_phys_to_virt(phys_addr_t phys)
 +{
 +      return __hyp_va(phys);
 +}
 +
 +static inline phys_addr_t hyp_virt_to_phys(void *addr)
 +{
 +      return __hyp_pa(addr);
 +}
 +
 +#define hyp_phys_to_pfn(phys) ((phys) >> PAGE_SHIFT)
 +#define hyp_pfn_to_phys(pfn)  ((phys_addr_t)((pfn) << PAGE_SHIFT))
 +#define hyp_phys_to_page(phys)        (&hyp_vmemmap[hyp_phys_to_pfn(phys)])
 +#define hyp_virt_to_page(virt)        hyp_phys_to_page(__hyp_pa(virt))
 +#define hyp_virt_to_pfn(virt) hyp_phys_to_pfn(__hyp_pa(virt))
 +
 +#define hyp_page_to_pfn(page) ((struct hyp_page *)(page) - hyp_vmemmap)
 +#define hyp_page_to_phys(page)  hyp_pfn_to_phys((hyp_page_to_pfn(page)))
 +#define hyp_page_to_virt(page)        __hyp_va(hyp_page_to_phys(page))
 +#define hyp_page_to_pool(page)        (((struct hyp_page *)page)->pool)
 +
 +static inline int hyp_page_count(void *addr)
 +{
 +      struct hyp_page *p = hyp_virt_to_page(addr);
 +
 +      return p->refcount;
 +}
 +
 +#endif /* __KVM_HYP_MEMORY_H */
@@@ -241,16 -177,7 +241,16 @@@ void handle_trap(struct kvm_cpu_contex
        case ESR_ELx_EC_SMC64:
                handle_host_smc(host_ctxt);
                break;
 +      case ESR_ELx_EC_SVE:
 +              sysreg_clear_set(cptr_el2, CPTR_EL2_TZ, 0);
 +              isb();
 +              sve_cond_update_zcr_vq(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2);
 +              break;
 +      case ESR_ELx_EC_IABT_LOW:
 +      case ESR_ELx_EC_DABT_LOW:
 +              handle_host_mem_abort(host_ctxt);
 +              break;
        default:
-               hyp_panic();
+               BUG();
        }
  }
index f4f364a,0000000..e342f7f
mode 100644,000000..100644
--- /dev/null
@@@ -1,281 -1,0 +1,279 @@@
-       if (!__get_fault_info(esr, &fault))
-               hyp_panic();
 +// SPDX-License-Identifier: GPL-2.0-only
 +/*
 + * Copyright (C) 2020 Google LLC
 + * Author: Quentin Perret <qperret@google.com>
 + */
 +
 +#include <linux/kvm_host.h>
 +#include <asm/kvm_emulate.h>
 +#include <asm/kvm_hyp.h>
 +#include <asm/kvm_mmu.h>
 +#include <asm/kvm_pgtable.h>
 +#include <asm/stage2_pgtable.h>
 +
 +#include <hyp/switch.h>
 +
 +#include <nvhe/gfp.h>
 +#include <nvhe/memory.h>
 +#include <nvhe/mem_protect.h>
 +#include <nvhe/mm.h>
 +
 +#define KVM_HOST_S2_FLAGS (KVM_PGTABLE_S2_NOFWB | KVM_PGTABLE_S2_IDMAP)
 +
 +extern unsigned long hyp_nr_cpus;
 +struct host_kvm host_kvm;
 +
 +struct hyp_pool host_s2_mem;
 +struct hyp_pool host_s2_dev;
 +
 +/*
 + * Copies of the host's CPU features registers holding sanitized values.
 + */
 +u64 id_aa64mmfr0_el1_sys_val;
 +u64 id_aa64mmfr1_el1_sys_val;
 +
 +static const u8 pkvm_hyp_id = 1;
 +
 +static void *host_s2_zalloc_pages_exact(size_t size)
 +{
 +      return hyp_alloc_pages(&host_s2_mem, get_order(size));
 +}
 +
 +static void *host_s2_zalloc_page(void *pool)
 +{
 +      return hyp_alloc_pages(pool, 0);
 +}
 +
 +static int prepare_s2_pools(void *mem_pgt_pool, void *dev_pgt_pool)
 +{
 +      unsigned long nr_pages, pfn;
 +      int ret;
 +
 +      pfn = hyp_virt_to_pfn(mem_pgt_pool);
 +      nr_pages = host_s2_mem_pgtable_pages();
 +      ret = hyp_pool_init(&host_s2_mem, pfn, nr_pages, 0);
 +      if (ret)
 +              return ret;
 +
 +      pfn = hyp_virt_to_pfn(dev_pgt_pool);
 +      nr_pages = host_s2_dev_pgtable_pages();
 +      ret = hyp_pool_init(&host_s2_dev, pfn, nr_pages, 0);
 +      if (ret)
 +              return ret;
 +
 +      host_kvm.mm_ops = (struct kvm_pgtable_mm_ops) {
 +              .zalloc_pages_exact = host_s2_zalloc_pages_exact,
 +              .zalloc_page = host_s2_zalloc_page,
 +              .phys_to_virt = hyp_phys_to_virt,
 +              .virt_to_phys = hyp_virt_to_phys,
 +              .page_count = hyp_page_count,
 +              .get_page = hyp_get_page,
 +              .put_page = hyp_put_page,
 +      };
 +
 +      return 0;
 +}
 +
 +static void prepare_host_vtcr(void)
 +{
 +      u32 parange, phys_shift;
 +
 +      /* The host stage 2 is id-mapped, so use parange for T0SZ */
 +      parange = kvm_get_parange(id_aa64mmfr0_el1_sys_val);
 +      phys_shift = id_aa64mmfr0_parange_to_phys_shift(parange);
 +
 +      host_kvm.arch.vtcr = kvm_get_vtcr(id_aa64mmfr0_el1_sys_val,
 +                                        id_aa64mmfr1_el1_sys_val, phys_shift);
 +}
 +
 +int kvm_host_prepare_stage2(void *mem_pgt_pool, void *dev_pgt_pool)
 +{
 +      struct kvm_s2_mmu *mmu = &host_kvm.arch.mmu;
 +      int ret;
 +
 +      prepare_host_vtcr();
 +      hyp_spin_lock_init(&host_kvm.lock);
 +
 +      ret = prepare_s2_pools(mem_pgt_pool, dev_pgt_pool);
 +      if (ret)
 +              return ret;
 +
 +      ret = kvm_pgtable_stage2_init_flags(&host_kvm.pgt, &host_kvm.arch,
 +                                          &host_kvm.mm_ops, KVM_HOST_S2_FLAGS);
 +      if (ret)
 +              return ret;
 +
 +      mmu->pgd_phys = __hyp_pa(host_kvm.pgt.pgd);
 +      mmu->arch = &host_kvm.arch;
 +      mmu->pgt = &host_kvm.pgt;
 +      mmu->vmid.vmid_gen = 0;
 +      mmu->vmid.vmid = 0;
 +
 +      return 0;
 +}
 +
 +int __pkvm_prot_finalize(void)
 +{
 +      struct kvm_s2_mmu *mmu = &host_kvm.arch.mmu;
 +      struct kvm_nvhe_init_params *params = this_cpu_ptr(&kvm_init_params);
 +
 +      params->vttbr = kvm_get_vttbr(mmu);
 +      params->vtcr = host_kvm.arch.vtcr;
 +      params->hcr_el2 |= HCR_VM;
 +      kvm_flush_dcache_to_poc(params, sizeof(*params));
 +
 +      write_sysreg(params->hcr_el2, hcr_el2);
 +      __load_stage2(&host_kvm.arch.mmu, host_kvm.arch.vtcr);
 +
 +      /*
 +       * Make sure to have an ISB before the TLB maintenance below but only
 +       * when __load_stage2() doesn't include one already.
 +       */
 +      asm(ALTERNATIVE("isb", "nop", ARM64_WORKAROUND_SPECULATIVE_AT));
 +
 +      /* Invalidate stale HCR bits that may be cached in TLBs */
 +      __tlbi(vmalls12e1);
 +      dsb(nsh);
 +      isb();
 +
 +      return 0;
 +}
 +
 +static int host_stage2_unmap_dev_all(void)
 +{
 +      struct kvm_pgtable *pgt = &host_kvm.pgt;
 +      struct memblock_region *reg;
 +      u64 addr = 0;
 +      int i, ret;
 +
 +      /* Unmap all non-memory regions to recycle the pages */
 +      for (i = 0; i < hyp_memblock_nr; i++, addr = reg->base + reg->size) {
 +              reg = &hyp_memory[i];
 +              ret = kvm_pgtable_stage2_unmap(pgt, addr, reg->base - addr);
 +              if (ret)
 +                      return ret;
 +      }
 +      return kvm_pgtable_stage2_unmap(pgt, addr, BIT(pgt->ia_bits) - addr);
 +}
 +
 +static bool find_mem_range(phys_addr_t addr, struct kvm_mem_range *range)
 +{
 +      int cur, left = 0, right = hyp_memblock_nr;
 +      struct memblock_region *reg;
 +      phys_addr_t end;
 +
 +      range->start = 0;
 +      range->end = ULONG_MAX;
 +
 +      /* The list of memblock regions is sorted, binary search it */
 +      while (left < right) {
 +              cur = (left + right) >> 1;
 +              reg = &hyp_memory[cur];
 +              end = reg->base + reg->size;
 +              if (addr < reg->base) {
 +                      right = cur;
 +                      range->end = reg->base;
 +              } else if (addr >= end) {
 +                      left = cur + 1;
 +                      range->start = end;
 +              } else {
 +                      range->start = reg->base;
 +                      range->end = end;
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +static bool range_is_memory(u64 start, u64 end)
 +{
 +      struct kvm_mem_range r1, r2;
 +
 +      if (!find_mem_range(start, &r1) || !find_mem_range(end, &r2))
 +              return false;
 +      if (r1.start != r2.start)
 +              return false;
 +
 +      return true;
 +}
 +
 +static inline int __host_stage2_idmap(u64 start, u64 end,
 +                                    enum kvm_pgtable_prot prot,
 +                                    struct hyp_pool *pool)
 +{
 +      return kvm_pgtable_stage2_map(&host_kvm.pgt, start, end - start, start,
 +                                    prot, pool);
 +}
 +
 +static int host_stage2_idmap(u64 addr)
 +{
 +      enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_W;
 +      struct kvm_mem_range range;
 +      bool is_memory = find_mem_range(addr, &range);
 +      struct hyp_pool *pool = is_memory ? &host_s2_mem : &host_s2_dev;
 +      int ret;
 +
 +      if (is_memory)
 +              prot |= KVM_PGTABLE_PROT_X;
 +
 +      hyp_spin_lock(&host_kvm.lock);
 +      ret = kvm_pgtable_stage2_find_range(&host_kvm.pgt, addr, prot, &range);
 +      if (ret)
 +              goto unlock;
 +
 +      ret = __host_stage2_idmap(range.start, range.end, prot, pool);
 +      if (is_memory || ret != -ENOMEM)
 +              goto unlock;
 +
 +      /*
 +       * host_s2_mem has been provided with enough pages to cover all of
 +       * memory with page granularity, so we should never hit the ENOMEM case.
 +       * However, it is difficult to know how much of the MMIO range we will
 +       * need to cover upfront, so we may need to 'recycle' the pages if we
 +       * run out.
 +       */
 +      ret = host_stage2_unmap_dev_all();
 +      if (ret)
 +              goto unlock;
 +
 +      ret = __host_stage2_idmap(range.start, range.end, prot, pool);
 +
 +unlock:
 +      hyp_spin_unlock(&host_kvm.lock);
 +
 +      return ret;
 +}
 +
 +int __pkvm_mark_hyp(phys_addr_t start, phys_addr_t end)
 +{
 +      int ret;
 +
 +      /*
 +       * host_stage2_unmap_dev_all() currently relies on MMIO mappings being
 +       * non-persistent, so don't allow changing page ownership in MMIO range.
 +       */
 +      if (!range_is_memory(start, end))
 +              return -EINVAL;
 +
 +      hyp_spin_lock(&host_kvm.lock);
 +      ret = kvm_pgtable_stage2_set_owner(&host_kvm.pgt, start, end - start,
 +                                         &host_s2_mem, pkvm_hyp_id);
 +      hyp_spin_unlock(&host_kvm.lock);
 +
 +      return ret != -EAGAIN ? ret : 0;
 +}
 +
 +void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt)
 +{
 +      struct kvm_vcpu_fault_info fault;
 +      u64 esr, addr;
 +      int ret = 0;
 +
 +      esr = read_sysreg_el2(SYS_ESR);
-       if (ret && ret != -EAGAIN)
-               hyp_panic();
++      BUG_ON(!__get_fault_info(esr, &fault));
 +
 +      addr = (fault.hpfar_el2 & HPFAR_MASK) << 8;
 +      ret = host_stage2_idmap(addr);
++      BUG_ON(ret && ret != -EAGAIN);
 +}