Merge branch kvm-arm64/nv-prefix into kvmarm/next
authorOliver Upton <oliver.upton@linux.dev>
Mon, 13 Feb 2023 23:33:41 +0000 (23:33 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Mon, 13 Feb 2023 23:33:41 +0000 (23:33 +0000)
* kvm-arm64/nv-prefix:
  : Preamble to NV support, courtesy of Marc Zyngier.
  :
  : This brings in a set of prerequisite patches for supporting nested
  : virtualization in KVM/arm64. Of course, there is a long way to go until
  : NV is actually enabled in KVM.
  :
  :  - Introduce cpucap / vCPU feature flag to pivot the NV code on
  :
  :  - Add support for EL2 vCPU register state
  :
  :  - Basic nested exception handling
  :
  :  - Hide unsupported features from the ID registers for NV-capable VMs
  KVM: arm64: nv: Use reg_to_encoding() to get sysreg ID
  KVM: arm64: nv: Only toggle cache for virtual EL2 when SCTLR_EL2 changes
  KVM: arm64: nv: Filter out unsupported features from ID regs
  KVM: arm64: nv: Emulate EL12 register accesses from the virtual EL2
  KVM: arm64: nv: Allow a sysreg to be hidden from userspace only
  KVM: arm64: nv: Emulate PSTATE.M for a guest hypervisor
  KVM: arm64: nv: Add accessors for SPSR_EL1, ELR_EL1 and VBAR_EL1 from virtual EL2
  KVM: arm64: nv: Handle SMCs taken from virtual EL2
  KVM: arm64: nv: Handle trapped ERET from virtual EL2
  KVM: arm64: nv: Inject HVC exceptions to the virtual EL2
  KVM: arm64: nv: Support virtual EL2 exceptions
  KVM: arm64: nv: Handle HCR_EL2.NV system register traps
  KVM: arm64: nv: Add nested virt VCPU primitives for vEL2 VCPU state
  KVM: arm64: nv: Add EL2 system registers to vcpu context
  KVM: arm64: nv: Allow userspace to set PSR_MODE_EL2x
  KVM: arm64: nv: Reset VCPU to EL2 registers if VCPU nested virt is set
  KVM: arm64: nv: Introduce nested virtualization VCPU feature
  KVM: arm64: Use the S2 MMU context to iterate over S2 table
  arm64: Add ARM64_HAS_NESTED_VIRT cpufeature

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
14 files changed:
1  2 
arch/arm64/include/asm/esr.h
arch/arm64/include/asm/kvm_arm.h
arch/arm64/include/asm/kvm_emulate.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/include/asm/sysreg.h
arch/arm64/kernel/cpufeature.c
arch/arm64/kvm/arm.c
arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h
arch/arm64/kvm/hyp/vhe/switch.c
arch/arm64/kvm/mmu.c
arch/arm64/kvm/reset.c
arch/arm64/kvm/sys_regs.c
arch/arm64/tools/cpucaps

Simple merge
        ECN(SP_ALIGN), ECN(FP_EXC32), ECN(FP_EXC64), ECN(SERROR), \
        ECN(BREAKPT_LOW), ECN(BREAKPT_CUR), ECN(SOFTSTP_LOW), \
        ECN(SOFTSTP_CUR), ECN(WATCHPT_LOW), ECN(WATCHPT_CUR), \
-       ECN(BKPT32), ECN(VECTOR32), ECN(BRK64)
+       ECN(BKPT32), ECN(VECTOR32), ECN(BRK64), ECN(ERET)
  
 -#define CPACR_EL1_TTA         (1 << 28)
  #define CPACR_EL1_DEFAULT     (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN |\
                                 CPACR_EL1_ZEN_EL1EN)
  
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -85,96 -85,36 +86,108 @@@ void vcpu_write_sys_reg(struct kvm_vcp
  /* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */
  #define CSSELR_MAX 14
  
 +/*
 + * Returns the minimum line size for the selected cache, expressed as
 + * Log2(bytes).
 + */
 +static u8 get_min_cache_line_size(bool icache)
 +{
 +      u64 ctr = read_sanitised_ftr_reg(SYS_CTR_EL0);
 +      u8 field;
 +
 +      if (icache)
 +              field = SYS_FIELD_GET(CTR_EL0, IminLine, ctr);
 +      else
 +              field = SYS_FIELD_GET(CTR_EL0, DminLine, ctr);
 +
 +      /*
 +       * Cache line size is represented as Log2(words) in CTR_EL0.
 +       * Log2(bytes) can be derived with the following:
 +       *
 +       * Log2(words) + 2 = Log2(bytes / 4) + 2
 +       *                 = Log2(bytes) - 2 + 2
 +       *                 = Log2(bytes)
 +       */
 +      return field + 2;
 +}
 +
  /* Which cache CCSIDR represents depends on CSSELR value. */
 -static u32 get_ccsidr(u32 csselr)
 +static u32 get_ccsidr(struct kvm_vcpu *vcpu, u32 csselr)
 +{
 +      u8 line_size;
 +
 +      if (vcpu->arch.ccsidr)
 +              return vcpu->arch.ccsidr[csselr];
 +
 +      line_size = get_min_cache_line_size(csselr & CSSELR_EL1_InD);
 +
 +      /*
 +       * Fabricate a CCSIDR value as the overriding value does not exist.
 +       * The real CCSIDR value will not be used as it can vary by the
 +       * physical CPU which the vcpu currently resides in.
 +       *
 +       * The line size is determined with get_min_cache_line_size(), which
 +       * should be valid for all CPUs even if they have different cache
 +       * configuration.
 +       *
 +       * The associativity bits are cleared, meaning the geometry of all data
 +       * and unified caches (which are guaranteed to be PIPT and thus
 +       * non-aliasing) are 1 set and 1 way.
 +       * Guests should not be doing cache operations by set/way at all, and
 +       * for this reason, we trap them and attempt to infer the intent, so
 +       * that we can flush the entire guest's address space at the appropriate
 +       * time. The exposed geometry minimizes the number of the traps.
 +       * [If guests should attempt to infer aliasing properties from the
 +       * geometry (which is not permitted by the architecture), they would
 +       * only do so for virtually indexed caches.]
 +       *
 +       * We don't check if the cache level exists as it is allowed to return
 +       * an UNKNOWN value if not.
 +       */
 +      return SYS_FIELD_PREP(CCSIDR_EL1, LineSize, line_size - 4);
 +}
 +
 +static int set_ccsidr(struct kvm_vcpu *vcpu, u32 csselr, u32 val)
  {
 -      u32 ccsidr;
 +      u8 line_size = FIELD_GET(CCSIDR_EL1_LineSize, val) + 4;
 +      u32 *ccsidr = vcpu->arch.ccsidr;
 +      u32 i;
 +
 +      if ((val & CCSIDR_EL1_RES0) ||
 +          line_size < get_min_cache_line_size(csselr & CSSELR_EL1_InD))
 +              return -EINVAL;
 +
 +      if (!ccsidr) {
 +              if (val == get_ccsidr(vcpu, csselr))
 +                      return 0;
 +
 +              ccsidr = kmalloc_array(CSSELR_MAX, sizeof(u32), GFP_KERNEL_ACCOUNT);
 +              if (!ccsidr)
 +                      return -ENOMEM;
  
 -      /* Make sure noone else changes CSSELR during this! */
 -      local_irq_disable();
 -      write_sysreg(csselr, csselr_el1);
 -      isb();
 -      ccsidr = read_sysreg(ccsidr_el1);
 -      local_irq_enable();
 +              for (i = 0; i < CSSELR_MAX; i++)
 +                      ccsidr[i] = get_ccsidr(vcpu, i);
  
 -      return ccsidr;
 +              vcpu->arch.ccsidr = ccsidr;
 +      }
 +
 +      ccsidr[csselr] = val;
 +
 +      return 0;
  }
  
+ static bool access_rw(struct kvm_vcpu *vcpu,
+                     struct sys_reg_params *p,
+                     const struct sys_reg_desc *r)
+ {
+       if (p->is_write)
+               vcpu_write_sys_reg(vcpu, p->regval, r->reg);
+       else
+               p->regval = vcpu_read_sys_reg(vcpu, r->reg);
+       return true;
+ }
  /*
   * See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
   */
Simple merge