#define __ARM64_KVM_HOST_H__
 
 #include <linux/bitmap.h>
-#include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/jump_label.h>
 #include <linux/kvm_types.h>
 
 #define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS
 
+/* Will be incremented when KVM_ARM_VCPU_SVE is fully implemented: */
 #define KVM_VCPU_MAX_FEATURES 4
 
 #define KVM_REQ_SLEEP \
 
 DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
 
-static inline int kvm_arm_init_arch_resources(void) { return 0; }
+extern unsigned int kvm_sve_max_vl;
+int kvm_arm_init_arch_resources(void);
 
 int __attribute_const__ kvm_target_cpu(void);
 int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
+void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu);
 int kvm_arch_vm_ioctl_check_extension(struct kvm *kvm, long ext);
 void __extended_idmap_trampoline(phys_addr_t boot_pgd, phys_addr_t idmap_start);
 
 #define KVM_ARM64_HOST_SVE_IN_USE      (1 << 3) /* backup for host TIF_SVE */
 #define KVM_ARM64_HOST_SVE_ENABLED     (1 << 4) /* SVE enabled for EL0 */
 #define KVM_ARM64_GUEST_HAS_SVE                (1 << 5) /* SVE exposed to guest */
+#define KVM_ARM64_VCPU_SVE_FINALIZED   (1 << 6) /* SVE config completed */
 
 #define vcpu_has_sve(vcpu) (system_supports_sve() && \
                            ((vcpu)->arch.flags & KVM_ARM64_GUEST_HAS_SVE))
 
 static inline void kvm_arch_hardware_unsetup(void) {}
 static inline void kvm_arch_sync_events(struct kvm *kvm) {}
-static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
 static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
 static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
 
 
 int kvm_arm_setup_stage2(struct kvm *kvm, unsigned long type);
 
-#define kvm_arm_vcpu_finalize(vcpu, what) (-EINVAL)
-#define kvm_arm_vcpu_is_finalized(vcpu) true
+int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int what);
+bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
+
+#define kvm_arm_vcpu_sve_finalized(vcpu) \
+       ((vcpu)->arch.flags & KVM_ARM64_VCPU_SVE_FINALIZED)
 
 #endif /* __ARM64_KVM_HOST_H__ */
 
 #define KVM_ARM_VCPU_EL1_32BIT         1 /* CPU running a 32bit VM */
 #define KVM_ARM_VCPU_PSCI_0_2          2 /* CPU uses PSCI v0.2 */
 #define KVM_ARM_VCPU_PMU_V3            3 /* Support guest PMUv3 */
+#define KVM_ARM_VCPU_SVE               4 /* enable SVE for this CPU */
 
 struct kvm_vcpu_init {
        __u32 target;
                                         ((n) << 5) | (i))
 #define KVM_REG_ARM64_SVE_FFR(i)       KVM_REG_ARM64_SVE_PREG(16, i)
 
+/* Vector lengths pseudo-register: */
+#define KVM_REG_ARM64_SVE_VLS          (KVM_REG_ARM64 | KVM_REG_ARM64_SVE | \
+                                        KVM_REG_SIZE_U512 | 0xffff)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR      0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
 
        return err;
 }
 
+#define vq_word(vq) (((vq) - SVE_VQ_MIN) / 64)
+#define vq_mask(vq) ((u64)1 << ((vq) - SVE_VQ_MIN) % 64)
+
+static bool vq_present(
+       const u64 (*const vqs)[DIV_ROUND_UP(SVE_VQ_MAX - SVE_VQ_MIN + 1, 64)],
+       unsigned int vq)
+{
+       return (*vqs)[vq_word(vq)] & vq_mask(vq);
+}
+
+static int get_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       unsigned int max_vq, vq;
+       u64 vqs[DIV_ROUND_UP(SVE_VQ_MAX - SVE_VQ_MIN + 1, 64)];
+
+       if (WARN_ON(!sve_vl_valid(vcpu->arch.sve_max_vl)))
+               return -EINVAL;
+
+       memset(vqs, 0, sizeof(vqs));
+
+       max_vq = sve_vq_from_vl(vcpu->arch.sve_max_vl);
+       for (vq = SVE_VQ_MIN; vq <= max_vq; ++vq)
+               if (sve_vq_available(vq))
+                       vqs[vq_word(vq)] |= vq_mask(vq);
+
+       if (copy_to_user((void __user *)reg->addr, vqs, sizeof(vqs)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int set_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+       unsigned int max_vq, vq;
+       u64 vqs[DIV_ROUND_UP(SVE_VQ_MAX - SVE_VQ_MIN + 1, 64)];
+
+       if (kvm_arm_vcpu_sve_finalized(vcpu))
+               return -EPERM; /* too late! */
+
+       if (WARN_ON(vcpu->arch.sve_state))
+               return -EINVAL;
+
+       if (copy_from_user(vqs, (const void __user *)reg->addr, sizeof(vqs)))
+               return -EFAULT;
+
+       max_vq = 0;
+       for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; ++vq)
+               if (vq_present(&vqs, vq))
+                       max_vq = vq;
+
+       if (max_vq > sve_vq_from_vl(kvm_sve_max_vl))
+               return -EINVAL;
+
+       for (vq = SVE_VQ_MIN; vq <= max_vq; ++vq)
+               if (vq_present(&vqs, vq) != sve_vq_available(vq))
+                       return -EINVAL;
+
+       /* Can't run with no vector lengths at all: */
+       if (max_vq < SVE_VQ_MIN)
+               return -EINVAL;
+
+       /* vcpu->arch.sve_state will be alloc'd by kvm_vcpu_finalize_sve() */
+       vcpu->arch.sve_max_vl = sve_vl_from_vq(max_vq);
+
+       return 0;
+}
+
 #define SVE_REG_SLICE_SHIFT    0
 #define SVE_REG_SLICE_BITS     5
 #define SVE_REG_ID_SHIFT       (SVE_REG_SLICE_SHIFT + SVE_REG_SLICE_BITS)
        struct sve_state_reg_region region;
        char __user *uptr = (char __user *)reg->addr;
 
-       if (!vcpu_has_sve(vcpu) || sve_reg_to_region(®ion, vcpu, reg))
+       if (!vcpu_has_sve(vcpu))
+               return -ENOENT;
+
+       /* Handle the KVM_REG_ARM64_SVE_VLS pseudo-reg as a special case: */
+       if (reg->id == KVM_REG_ARM64_SVE_VLS)
+               return get_sve_vls(vcpu, reg);
+
+       /* Otherwise, reg is an architectural SVE register... */
+
+       if (!kvm_arm_vcpu_sve_finalized(vcpu))
+               return -EPERM;
+
+       if (sve_reg_to_region(®ion, vcpu, reg))
                return -ENOENT;
 
        if (copy_to_user(uptr, vcpu->arch.sve_state + region.koffset,
        struct sve_state_reg_region region;
        const char __user *uptr = (const char __user *)reg->addr;
 
-       if (!vcpu_has_sve(vcpu) || sve_reg_to_region(®ion, vcpu, reg))
+       if (!vcpu_has_sve(vcpu))
+               return -ENOENT;
+
+       /* Handle the KVM_REG_ARM64_SVE_VLS pseudo-reg as a special case: */
+       if (reg->id == KVM_REG_ARM64_SVE_VLS)
+               return set_sve_vls(vcpu, reg);
+
+       /* Otherwise, reg is an architectural SVE register... */
+
+       if (!kvm_arm_vcpu_sve_finalized(vcpu))
+               return -EPERM;
+
+       if (sve_reg_to_region(®ion, vcpu, reg))
                return -ENOENT;
 
        if (copy_from_user(vcpu->arch.sve_state + region.koffset, uptr,
        if (!vcpu_has_sve(vcpu))
                return 0;
 
-       return slices * (SVE_NUM_PREGS + SVE_NUM_ZREGS + 1 /* FFR */);
+       /* Policed by KVM_GET_REG_LIST: */
+       WARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));
+
+       return slices * (SVE_NUM_PREGS + SVE_NUM_ZREGS + 1 /* FFR */)
+               + 1; /* KVM_REG_ARM64_SVE_VLS */
 }
 
 static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu,
        if (!vcpu_has_sve(vcpu))
                return 0;
 
+       /* Policed by KVM_GET_REG_LIST: */
+       WARN_ON(!kvm_arm_vcpu_sve_finalized(vcpu));
+
+       /*
+        * Enumerate this first, so that userspace can save/restore in
+        * the order reported by KVM_GET_REG_LIST:
+        */
+       reg = KVM_REG_ARM64_SVE_VLS;
+       if (put_user(reg, uindices++))
+               return -EFAULT;
+
+       ++num_regs;
+
        for (i = 0; i < slices; i++) {
                for (n = 0; n < SVE_NUM_ZREGS; n++) {
                        reg = KVM_REG_ARM64_SVE_ZREG(n, i);
 
 #include <linux/kvm_host.h>
 #include <linux/kvm.h>
 #include <linux/hw_breakpoint.h>
+#include <linux/slab.h>
+#include <linux/types.h>
 
 #include <kvm/arm_arch_timer.h>
 
 #include <asm/cpufeature.h>
 #include <asm/cputype.h>
+#include <asm/fpsimd.h>
 #include <asm/ptrace.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_asm.h>
        return r;
 }
 
+unsigned int kvm_sve_max_vl;
+
+int kvm_arm_init_arch_resources(void)
+{
+       if (system_supports_sve()) {
+               kvm_sve_max_vl = sve_max_virtualisable_vl;
+
+               /*
+                * The get_sve_reg()/set_sve_reg() ioctl interface will need
+                * to be extended with multiple register slice support in
+                * order to support vector lengths greater than
+                * SVE_VL_ARCH_MAX:
+                */
+               if (WARN_ON(kvm_sve_max_vl > SVE_VL_ARCH_MAX))
+                       kvm_sve_max_vl = SVE_VL_ARCH_MAX;
+
+               /*
+                * Don't even try to make use of vector lengths that
+                * aren't available on all CPUs, for now:
+                */
+               if (kvm_sve_max_vl < sve_max_vl)
+                       pr_warn("KVM: SVE vector length for guests limited to %u bytes\n",
+                               kvm_sve_max_vl);
+       }
+
+       return 0;
+}
+
+/*
+ * Finalize vcpu's maximum SVE vector length, allocating
+ * vcpu->arch.sve_state as necessary.
+ */
+static int kvm_vcpu_finalize_sve(struct kvm_vcpu *vcpu)
+{
+       void *buf;
+       unsigned int vl;
+
+       vl = vcpu->arch.sve_max_vl;
+
+       /*
+        * Resposibility for these properties is shared between
+        * kvm_arm_init_arch_resources(), kvm_vcpu_enable_sve() and
+        * set_sve_vls().  Double-check here just to be sure:
+        */
+       if (WARN_ON(!sve_vl_valid(vl) || vl > sve_max_virtualisable_vl ||
+                   vl > SVE_VL_ARCH_MAX))
+               return -EIO;
+
+       buf = kzalloc(SVE_SIG_REGS_SIZE(sve_vq_from_vl(vl)), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       vcpu->arch.sve_state = buf;
+       vcpu->arch.flags |= KVM_ARM64_VCPU_SVE_FINALIZED;
+       return 0;
+}
+
+int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int what)
+{
+       switch (what) {
+       case KVM_ARM_VCPU_SVE:
+               if (!vcpu_has_sve(vcpu))
+                       return -EINVAL;
+
+               if (kvm_arm_vcpu_sve_finalized(vcpu))
+                       return -EPERM;
+
+               return kvm_vcpu_finalize_sve(vcpu);
+       }
+
+       return -EINVAL;
+}
+
+bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu)
+{
+       if (vcpu_has_sve(vcpu) && !kvm_arm_vcpu_sve_finalized(vcpu))
+               return false;
+
+       return true;
+}
+
+void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
+{
+       kfree(vcpu->arch.sve_state);
+}
+
 /**
  * kvm_reset_vcpu - sets core registers and sys_regs to reset value
  * @vcpu: The VCPU pointer