[RFC PATCH v3 10/16] KVM: arm64: Add a new VM device control group for SPE
Haibo Xu
haibo.xu at linaro.org
Thu Nov 5 05:10:54 EST 2020
On Wed, 28 Oct 2020 at 01:26, Alexandru Elisei <alexandru.elisei at arm.com> wrote:
>
> Stage 2 faults triggered by the profiling buffer attempting to write to
> memory are reported by the SPE hardware by asserting a buffer management
> event interrupt. Interrupts are by their nature asynchronous, which means
> that the guest might have changed its stage 1 translation tables since the
> attempted write. SPE reports the guest virtual address that caused the data
> abort, but not the IPA, which means that KVM would have to walk the guest's
> stage 1 tables to find the IPA; using the AT instruction to walk the
> guest's tables in hardware is not an option because it doesn't report the
> IPA in the case of a stage 2 fault on a stage 1 table walk.
>
> Fix both problems by pre-mapping the guest's memory at stage 2 with write
> permissions to avoid any faults. Userspace calls mlock() on the VMAs that
> back the guest's memory, pinning the pages in memory, then tells KVM to map
> the memory at stage 2 by using the VM control group KVM_ARM_VM_SPE_CTRL
> with the attribute KVM_ARM_VM_SPE_FINALIZE. KVM will map all writable VMAs
> which have the VM_LOCKED flag set. Hugetlb VMAs are practically pinned in
> memory after they are faulted in and mlock() doesn't set the VM_LOCKED
> flag, and just faults the pages in; KVM will treat hugetlb VMAs like they
> have the VM_LOCKED flag and will also map them, faulting them in if
> necessary, when handling the ioctl.
>
> VM live migration relies on a bitmap of dirty pages. This bitmap is created
> by write-protecting a memslot and updating it as KVM handles stage 2 write
> faults. Because KVM cannot handle stage 2 faults reported by the profiling
> buffer, it will not pre-map a logging memslot. This effectively means that
> profiling is not available when the VM is configured for live migration.
>
> Signed-off-by: Alexandru Elisei <alexandru.elisei at arm.com>
> ---
> Documentation/virt/kvm/devices/vm.rst | 28 +++++
> arch/arm64/include/asm/kvm_host.h | 5 +
> arch/arm64/include/asm/kvm_mmu.h | 2 +
> arch/arm64/include/uapi/asm/kvm.h | 3 +
> arch/arm64/kvm/arm.c | 78 +++++++++++-
> arch/arm64/kvm/guest.c | 48 ++++++++
> arch/arm64/kvm/mmu.c | 169 ++++++++++++++++++++++++++
> arch/arm64/kvm/spe.c | 81 ++++++++++++
> include/kvm/arm_spe.h | 36 ++++++
> 9 files changed, 448 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/virt/kvm/devices/vm.rst b/Documentation/virt/kvm/devices/vm.rst
> index 0aa5b1cfd700..b70798a72d8a 100644
> --- a/Documentation/virt/kvm/devices/vm.rst
> +++ b/Documentation/virt/kvm/devices/vm.rst
> @@ -314,3 +314,31 @@ Allows userspace to query the status of migration mode.
> if it is enabled
> :Returns: -EFAULT if the given address is not accessible from kernel space;
> 0 in case of success.
> +
> +6. GROUP: KVM_ARM_VM_SPE_CTRL
> +===============================
> +
> +:Architectures: arm64
> +
> +6.1. ATTRIBUTE: KVM_ARM_VM_SPE_FINALIZE
> +-----------------------------------------
> +
> +Finalizes the creation of the SPE feature by mapping the guest memory in the
> +stage 2 table. Guest memory must be readable, writable and pinned in RAM, which
> +is achieved with an mlock() system call; the memory can be backed by a hugetlbfs
> +file. Memory regions from read-only or dirty page logging enabled memslots will
> +be ignored. After the call, no changes to the guest memory, including to its
> +contents, are permitted.
> +
> +Subsequent KVM_ARM_VCPU_INIT calls will cause the memory to become unmapped and
> +the feature must be finalized again before any VCPU can run.
> +
> +If any VCPUs are run before finalizing the feature, KVM_RUN will return -EPERM.
> +
> +:Parameters: none
> +:Returns: -EAGAIN if guest memory has been modified while the call was
> + executing
> + -EBUSY if the feature is already initialized
> + -EFAULT if an address backing the guest memory is invalid
> + -ENXIO if SPE is not supported or not properly configured
> + 0 in case of success
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 5b68c06930c6..27f581750c6e 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -92,6 +92,7 @@ struct kvm_s2_mmu {
>
> struct kvm_arch {
> struct kvm_s2_mmu mmu;
> + struct kvm_spe spe;
>
> /* VTCR_EL2 value for this VM */
> u64 vtcr;
> @@ -612,6 +613,10 @@ void kvm_arm_vcpu_init_debug(struct kvm_vcpu *vcpu);
> void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
> void kvm_arm_clear_debug(struct kvm_vcpu *vcpu);
> void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu);
> +int kvm_arm_vm_arch_set_attr(struct kvm *kvm, struct kvm_device_attr *attr);
> +int kvm_arm_vm_arch_get_attr(struct kvm *kvm, struct kvm_device_attr *attr);
> +int kvm_arm_vm_arch_has_attr(struct kvm *kvm, struct kvm_device_attr *attr);
> +
> int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
> struct kvm_device_attr *attr);
> int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
> diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
> index 331394306cce..bad94662bbed 100644
> --- a/arch/arm64/include/asm/kvm_mmu.h
> +++ b/arch/arm64/include/asm/kvm_mmu.h
> @@ -124,6 +124,8 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu);
> void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu);
> int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
> phys_addr_t pa, unsigned long size, bool writable);
> +int kvm_map_locked_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot,
> + enum kvm_pgtable_prot prot);
>
> int kvm_handle_guest_abort(struct kvm_vcpu *vcpu);
>
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index ca57dfb7abf0..8876e564ba56 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -350,6 +350,9 @@ struct kvm_vcpu_events {
> #define KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3
> #define KVM_DEV_ARM_ITS_CTRL_RESET 4
>
> +#define KVM_ARM_VM_SPE_CTRL 0
> +#define KVM_ARM_VM_SPE_FINALIZE 0
> +
> /* Device Control API on vcpu fd */
> #define KVM_ARM_VCPU_PMU_V3_CTRL 0
> #define KVM_ARM_VCPU_PMU_V3_IRQ 0
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index e51d8f328c7e..2d98248f2c66 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -41,6 +41,7 @@
> #include <kvm/arm_hypercalls.h>
> #include <kvm/arm_pmu.h>
> #include <kvm/arm_psci.h>
> +#include <kvm/arm_spe.h>
>
> #ifdef REQUIRES_VIRT
> __asm__(".arch_extension virt");
> @@ -653,6 +654,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
> if (unlikely(!kvm_vcpu_initialized(vcpu)))
> return -ENOEXEC;
>
> + if (vcpu_has_spe(vcpu) && unlikely(!kvm_arm_spe_finalized(vcpu->kvm)))
> + return -EPERM;
> +
> ret = kvm_vcpu_first_run_init(vcpu);
> if (ret)
> return ret;
> @@ -982,12 +986,22 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
> * ensuring that the data side is always coherent. We still
> * need to invalidate the I-cache though, as FWB does *not*
> * imply CTR_EL0.DIC.
> + *
> + * If the guest has SPE, we need to unmap the entire address space to
> + * allow for any changes to the VM memory made by userspace to propagate
> + * to the stage 2 tables when SPE is re-finalized; this also makes sure
> + * we keep the userspace and the guest's view of the memory contents
> + * synchronized.
> */
> if (vcpu->arch.has_run_once) {
> - if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
> + if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB) ||
> + vcpu_has_spe(vcpu)) {
> stage2_unmap_vm(vcpu->kvm);
> - else
> + if (vcpu_has_spe(vcpu))
> + kvm_arm_spe_notify_vcpu_init(vcpu);
> + } else {
> __flush_icache_all();
> + }
> }
>
> vcpu_reset_hcr(vcpu);
> @@ -1045,6 +1059,45 @@ static int kvm_arm_vcpu_has_attr(struct kvm_vcpu *vcpu,
> return ret;
> }
>
> +static int kvm_arm_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + int ret = -ENXIO;
> +
> + switch (attr->group) {
> + default:
> + ret = kvm_arm_vm_arch_set_attr(kvm, attr);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int kvm_arm_vm_get_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + int ret = -ENXIO;
> +
> + switch (attr->group) {
> + default:
> + ret = kvm_arm_vm_arch_get_attr(kvm, attr);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int kvm_arm_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + int ret = -ENXIO;
> +
> + switch (attr->group) {
> + default:
> + ret = kvm_arm_vm_arch_has_attr(kvm, attr);
> + break;
> + }
> +
> + return ret;
> +}
> +
> static int kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu,
> struct kvm_vcpu_events *events)
> {
> @@ -1259,6 +1312,27 @@ long kvm_arch_vm_ioctl(struct file *filp,
>
> return 0;
> }
> + case KVM_SET_DEVICE_ATTR: {
> + struct kvm_device_attr attr;
> +
> + if (copy_from_user(&attr, argp, sizeof(attr)))
> + return -EFAULT;
> + return kvm_arm_vm_set_attr(kvm, &attr);
> + }
> + case KVM_GET_DEVICE_ATTR: {
> + struct kvm_device_attr attr;
> +
> + if (copy_from_user(&attr, argp, sizeof(attr)))
> + return -EFAULT;
> + return kvm_arm_vm_get_attr(kvm, &attr);
> + }
> + case KVM_HAS_DEVICE_ATTR: {
> + struct kvm_device_attr attr;
> +
> + if (copy_from_user(&attr, argp, sizeof(attr)))
> + return -EFAULT;
> + return kvm_arm_vm_has_attr(kvm, &attr);
> + }
> default:
> return -EINVAL;
> }
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 2ba790eeb782..d0dc4bdb8b4a 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -988,3 +988,51 @@ int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu,
>
> return ret;
> }
> +
> +int kvm_arm_vm_arch_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + int ret;
> +
> + switch (attr->group) {
> + case KVM_ARM_VM_SPE_CTRL:
> + ret = kvm_arm_vm_spe_set_attr(kvm, attr);
> + break;
> + default:
> + ret = -ENXIO;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +int kvm_arm_vm_arch_get_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + int ret;
> +
> + switch (attr->group) {
> + case KVM_ARM_VM_SPE_CTRL:
> + ret = kvm_arm_vm_spe_get_attr(kvm, attr);
> + break;
> + default:
> + ret = -ENXIO;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +int kvm_arm_vm_arch_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + int ret;
> +
> + switch (attr->group) {
> + case KVM_ARM_VM_SPE_CTRL:
> + ret = kvm_arm_vm_spe_has_attr(kvm, attr);
> + break;
> + default:
> + ret = -ENXIO;
> + break;
> + }
> +
> + return ret;
> +}
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index c3c43555490d..31b2216a5881 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1365,6 +1365,175 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
> return ret;
> }
>
> +static int stage2_map_vma(struct kvm *kvm,
> + struct kvm_memory_slot *memslot,
> + struct vm_area_struct *vma,
> + enum kvm_pgtable_prot prot,
> + unsigned long mmu_seq, hva_t *hvap,
> + struct kvm_mmu_memory_cache *cache)
> +{
> + struct kvm_pgtable *pgt = kvm->arch.mmu.pgt;
> + unsigned long stage2_pagesize, remaining;
> + bool force_pte, writable;
> + hva_t hva, hva_end;
> + kvm_pfn_t pfn;
> + gpa_t gpa;
> + gfn_t gfn;
> + int ret;
> +
> + hva = max(memslot->userspace_addr, vma->vm_start);
> + hva_end = min(vma->vm_end, memslot->userspace_addr +
> + (memslot->npages << PAGE_SHIFT));
> +
> + gpa = (memslot->base_gfn << PAGE_SHIFT) + hva - memslot->userspace_addr;
> + gfn = gpa >> PAGE_SHIFT;
> +
> + stage2_pagesize = 1UL << stage2_max_pageshift(memslot, vma, hva, &force_pte);
> +
> + while (hva < hva_end) {
> + ret = kvm_mmu_topup_memory_cache(cache,
> + kvm_mmu_cache_min_pages(kvm));
> + if (ret)
> + return ret;
> +
> + /*
> + * We start mapping with the highest possible page size, so the
> + * gpa and gfn will always be properly aligned to the current
> + * page size.
> + */
> + pfn = __gfn_to_pfn_memslot(memslot, gfn, false, NULL, true, &writable);
> + if (pfn == KVM_PFN_ERR_HWPOISON)
> + return -EFAULT;
> + if (is_error_noslot_pfn(pfn))
> + return -EFAULT;
> + /* Can only happen if naughty userspace changed the VMA. */
> + if (kvm_is_device_pfn(pfn) || !writable)
> + return -EAGAIN;
> +
> + spin_lock(&kvm->mmu_lock);
> + if (mmu_notifier_retry(kvm, mmu_seq)) {
> + spin_unlock(&kvm->mmu_lock);
> + return -EAGAIN;
> + }
> +
> + remaining = hva_end - hva;
> + if (stage2_pagesize == PUD_SIZE && remaining < PUD_SIZE)
> + stage2_pagesize = PMD_SIZE;
> + if (stage2_pagesize == PMD_SIZE && remaining < PMD_SIZE) {
> + force_pte = true;
> + stage2_pagesize = PAGE_SIZE;
> + }
> +
> + if (!force_pte && stage2_pagesize == PAGE_SIZE)
> + /*
> + * The hva and gpa will always be PMD aligned if
> + * hva is backed by a transparent huge page. gpa will
> + * not be modified and it's not necessary to recompute
> + * hva.
> + */
> + stage2_pagesize = transparent_hugepage_adjust(memslot, hva, &pfn, &gpa);
> +
> + ret = kvm_pgtable_stage2_map(pgt, gpa, stage2_pagesize,
> + __pfn_to_phys(pfn), prot, cache);
> + spin_unlock(&kvm->mmu_lock);
> +
> + kvm_set_pfn_accessed(pfn);
> + kvm_release_pfn_dirty(pfn);
> +
> + if (ret)
> + return ret;
> + else if (hva < hva_end)
> + cond_resched();
> +
> + hva += stage2_pagesize;
> + gpa += stage2_pagesize;
> + gfn = gpa >> PAGE_SHIFT;
> + }
> +
> + *hvap = hva;
> + return 0;
> +}
> +
> +int kvm_map_locked_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot,
> + enum kvm_pgtable_prot prot)
> +{
> + struct kvm_mmu_memory_cache cache = { 0, __GFP_ZERO, NULL, };
> + struct vm_area_struct *vma;
> + unsigned long mmu_seq;
> + hva_t hva, hva_memslot_end;
> + int ret;
> +
> + lockdep_assert_held(&kvm->slots_lock);
> +
> + if (!(prot & KVM_PGTABLE_PROT_R))
> + return -EPERM;
> + if ((prot & KVM_PGTABLE_PROT_W) && (memslot->flags & KVM_MEM_READONLY))
> + return -EPERM;
> +
> + hva = memslot->userspace_addr;
> + hva_memslot_end = memslot->userspace_addr + (memslot->npages << PAGE_SHIFT);
> +
> + /*
> + * Be extra careful here in case userspace is messing with the VMAs
> + * backing the memslot.
> + */
> + mmu_seq = kvm->mmu_notifier_seq;
> + smp_rmb();
> +
> + /*
> + * A memslot might span multiple VMAs and any holes between them, while
> + * a VMA might span multiple memslots (see
> + * kvm_arch_prepare_memory_region()). Take the intersection of the VMAs
> + * with the memslot.
> + */
> + do {
> + mmap_read_lock(current->mm);
> + vma = find_vma(current->mm, hva);
> + /*
> + * find_vma() returns first VMA with hva < vma->vm_end, which
> + * means that it is possible for the VMA to start *after* the
> + * end of the memslot.
> + */
> + if (!vma || vma->vm_start >= hva_memslot_end) {
> + mmap_read_unlock(current->mm);
> + return 0;
> + }
> +
> + /*
> + * VM_LOCKED pages are put in the unevictable LRU list and
> + * hugetlb pages are not put in any LRU list; both will stay
> + * pinned in memory.
> + */
> + if (!(vma->vm_flags & VM_LOCKED) && !is_vm_hugetlb_page(vma)) {
> + /* Go to next VMA. */
> + hva = vma->vm_end;
> + mmap_read_unlock(current->mm);
> + continue;
> + }
> + if (!(vma->vm_flags & VM_READ) ||
> + ((prot & KVM_PGTABLE_PROT_W) && !(vma->vm_flags & VM_WRITE))) {
> + /* Go to next VMA. */
> + hva = vma->vm_end;
> + mmap_read_unlock(current->mm);
> + continue;
> + }
> + mmap_read_unlock(current->mm);
> +
> + ret = stage2_map_vma(kvm, memslot, vma, prot, mmu_seq, &hva, &cache);
> + if (ret)
> + return ret;
> + } while (hva < hva_memslot_end);
> +
> + if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB)) {
> + spin_lock(&kvm->mmu_lock);
> + stage2_flush_memslot(kvm, memslot);
> + spin_unlock(&kvm->mmu_lock);
> + }
> +
> + return 0;
> +}
> +
> +
> void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *slot)
> {
> }
> diff --git a/arch/arm64/kvm/spe.c b/arch/arm64/kvm/spe.c
> index f91a52cd7cd3..316ff8dfed5b 100644
> --- a/arch/arm64/kvm/spe.c
> +++ b/arch/arm64/kvm/spe.c
> @@ -10,6 +10,13 @@
> #include <kvm/arm_spe.h>
> #include <kvm/arm_vgic.h>
>
> +#include <asm/kvm_mmu.h>
> +
It seems that the below function is used to de-finalize the spe status
if I get it correctly.
How about rename the function to some like "kvm_arm_vcpu_init_spe_definalize()"
> +void kvm_arm_spe_notify_vcpu_init(struct kvm_vcpu *vcpu)
> +{
> + vcpu->kvm->arch.spe.finalized = false;
> +}
> +
> static bool kvm_arm_vcpu_supports_spe(struct kvm_vcpu *vcpu)
> {
> if (!vcpu_has_spe(vcpu))
> @@ -115,6 +122,50 @@ int kvm_arm_spe_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
> return -ENXIO;
> }
>
> +static int kvm_arm_spe_finalize(struct kvm *kvm)
> +{
> + struct kvm_memory_slot *memslot;
> + enum kvm_pgtable_prot prot;
> + struct kvm_vcpu *vcpu;
> + int i, ret;
> +
> + kvm_for_each_vcpu(i, vcpu, kvm) {
> + if (!kvm_arm_spe_vcpu_initialized(vcpu))
> + return -ENXIO;
> + }
> +
> + mutex_unlock(&kvm->slots_lock);
Should be mutex_lock(&kvm->slots_lock);?
> + if (kvm_arm_spe_finalized(kvm)) {
> + mutex_unlock(&kvm->slots_lock);
> + return -EBUSY;
> + }
> +
> + prot = KVM_PGTABLE_PROT_R | KVM_PGTABLE_PROT_W;
> + kvm_for_each_memslot(memslot, kvm_memslots(kvm)) {
> + /* Only map memory that SPE can write to. */
> + if (memslot->flags & KVM_MEM_READONLY)
> + continue;
> + /*
> + * Dirty page logging will write-protect pages, which breaks
> + * SPE.
> + */
> + if (memslot->dirty_bitmap)
> + continue;
> + ret = kvm_map_locked_memslot(kvm, memslot, prot);
> + if (ret)
> + break;
> + }
> +
> + if (!ret)
> + kvm->arch.spe.finalized = true;
> + mutex_unlock(&kvm->slots_lock);
> +
> + if (ret)
> + stage2_unmap_vm(kvm);
> +
> + return ret;
> +}
> +
> int kvm_arm_spe_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
> {
> switch (attr->attr) {
> @@ -127,3 +178,33 @@ int kvm_arm_spe_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
>
> return -ENXIO;
> }
> +
> +int kvm_arm_vm_spe_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + switch (attr->attr) {
> + case KVM_ARM_VM_SPE_FINALIZE:
> + return kvm_arm_spe_finalize(kvm);
> + }
> +
> + return -ENXIO;
> +}
> +
> +int kvm_arm_vm_spe_get_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + return -ENXIO;
> +}
> +
> +int kvm_arm_vm_spe_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
> +{
> + struct kvm_vcpu *vcpu;
> + int i;
> +
> + switch (attr->attr) {
> + case KVM_ARM_VM_SPE_FINALIZE:
> + kvm_for_each_vcpu(i, vcpu, kvm)
> + if (kvm_arm_vcpu_supports_spe(vcpu))
> + return 0;
> + }
> +
> + return -ENXIO;
> +}
> diff --git a/include/kvm/arm_spe.h b/include/kvm/arm_spe.h
> index 0275e8097529..7f9f3a03aadb 100644
> --- a/include/kvm/arm_spe.h
> +++ b/include/kvm/arm_spe.h
> @@ -18,23 +18,38 @@ struct kvm_spe_cpu {
> bool initialized; /* Feature is initialized on VCPU */
> };
>
> +struct kvm_spe {
> + bool finalized;
> +};
> +
> #define kvm_arm_spe_irq_initialized(v) \
> ((v)->arch.spe_cpu.irq_num >= VGIC_NR_SGIS && \
> (v)->arch.spe_cpu.irq_num < VGIC_MAX_PRIVATE)
> #define kvm_arm_spe_vcpu_initialized(v) ((v)->arch.spe_cpu.initialized)
> +#define kvm_arm_spe_finalized(k) ((k)->arch.spe.finalized)
>
> int kvm_arm_spe_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
> int kvm_arm_spe_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
> int kvm_arm_spe_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
>
> +int kvm_arm_vm_spe_set_attr(struct kvm *vcpu, struct kvm_device_attr *attr);
> +int kvm_arm_vm_spe_get_attr(struct kvm *vcpu, struct kvm_device_attr *attr);
> +int kvm_arm_vm_spe_has_attr(struct kvm *vcpu, struct kvm_device_attr *attr);
> +
> +void kvm_arm_spe_notify_vcpu_init(struct kvm_vcpu *vcpu);
> +
> #else
> #define kvm_arm_supports_spe() false
>
> struct kvm_spe_cpu {
> };
>
> +struct kvm_spe {
> +};
> +
> #define kvm_arm_spe_irq_initialized(v) false
> #define kvm_arm_spe_vcpu_initialized(v) false
> +#define kvm_arm_spe_finalized(k) false
>
> static inline int kvm_arm_spe_set_attr(struct kvm_vcpu *vcpu,
> struct kvm_device_attr *attr)
> @@ -51,5 +66,26 @@ static inline int kvm_arm_spe_has_attr(struct kvm_vcpu *vcpu,
> {
> return -ENXIO;
> }
> +
> +static inline int kvm_arm_vm_spe_set_attr(struct kvm *vcpu,
> + struct kvm_device_attr *attr)
> +{
> + return -ENXIO;
> +}
> +
> +static inline int kvm_arm_vm_spe_get_attr(struct kvm *vcpu,
> + struct kvm_device_attr *attr)
> +{
> + return -ENXIO;
> +}
> +
> +static inline int kvm_arm_vm_spe_has_attr(struct kvm *vcpu,
> + struct kvm_device_attr *attr)
> +{
> + return -ENXIO;
> +}
> +
> +static inline void kvm_arm_spe_notify_vcpu_init(struct kvm_vcpu *vcpu) {}
> +
> #endif /* CONFIG_KVM_ARM_SPE */
> #endif /* __ASM_ARM_KVM_SPE_H */
> --
> 2.29.1
>
> _______________________________________________
> kvmarm mailing list
> kvmarm at lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
More information about the linux-arm-kernel
mailing list