162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021 Western Digital Corporation or its affiliates. 462306a36Sopenharmony_ci * Copyright (C) 2022 Ventana Micro Systems Inc. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Anup Patel <apatel@ventanamicro.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bits.h> 1162306a36Sopenharmony_ci#include <linux/kvm_host.h> 1262306a36Sopenharmony_ci#include <linux/uaccess.h> 1362306a36Sopenharmony_ci#include <asm/kvm_aia_imsic.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic void unlock_vcpus(struct kvm *kvm, int vcpu_lock_idx) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct kvm_vcpu *tmp_vcpu; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) { 2062306a36Sopenharmony_ci tmp_vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx); 2162306a36Sopenharmony_ci mutex_unlock(&tmp_vcpu->mutex); 2262306a36Sopenharmony_ci } 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void unlock_all_vcpus(struct kvm *kvm) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci unlock_vcpus(kvm, atomic_read(&kvm->online_vcpus) - 1); 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic bool lock_all_vcpus(struct kvm *kvm) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct kvm_vcpu *tmp_vcpu; 3362306a36Sopenharmony_ci unsigned long c; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci kvm_for_each_vcpu(c, tmp_vcpu, kvm) { 3662306a36Sopenharmony_ci if (!mutex_trylock(&tmp_vcpu->mutex)) { 3762306a36Sopenharmony_ci unlock_vcpus(kvm, c - 1); 3862306a36Sopenharmony_ci return false; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return true; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int aia_create(struct kvm_device *dev, u32 type) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int ret; 4862306a36Sopenharmony_ci unsigned long i; 4962306a36Sopenharmony_ci struct kvm *kvm = dev->kvm; 5062306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (irqchip_in_kernel(kvm)) 5362306a36Sopenharmony_ci return -EEXIST; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ret = -EBUSY; 5662306a36Sopenharmony_ci if (!lock_all_vcpus(kvm)) 5762306a36Sopenharmony_ci return ret; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci kvm_for_each_vcpu(i, vcpu, kvm) { 6062306a36Sopenharmony_ci if (vcpu->arch.ran_atleast_once) 6162306a36Sopenharmony_ci goto out_unlock; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci ret = 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci kvm->arch.aia.in_kernel = true; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ciout_unlock: 6862306a36Sopenharmony_ci unlock_all_vcpus(kvm); 6962306a36Sopenharmony_ci return ret; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void aia_destroy(struct kvm_device *dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci kfree(dev); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int aia_config(struct kvm *kvm, unsigned long type, 7862306a36Sopenharmony_ci u32 *nr, bool write) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct kvm_aia *aia = &kvm->arch.aia; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Writes can only be done before irqchip is initialized */ 8362306a36Sopenharmony_ci if (write && kvm_riscv_aia_initialized(kvm)) 8462306a36Sopenharmony_ci return -EBUSY; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (type) { 8762306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_MODE: 8862306a36Sopenharmony_ci if (write) { 8962306a36Sopenharmony_ci switch (*nr) { 9062306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_MODE_EMUL: 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_MODE_HWACCEL: 9362306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_MODE_AUTO: 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * HW Acceleration and Auto modes only 9662306a36Sopenharmony_ci * supported on host with non-zero guest 9762306a36Sopenharmony_ci * external interrupts (i.e. non-zero 9862306a36Sopenharmony_ci * VS-level IMSIC pages). 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci if (!kvm_riscv_aia_nr_hgei) 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci default: 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci aia->mode = *nr; 10762306a36Sopenharmony_ci } else 10862306a36Sopenharmony_ci *nr = aia->mode; 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_IDS: 11162306a36Sopenharmony_ci if (write) { 11262306a36Sopenharmony_ci if ((*nr < KVM_DEV_RISCV_AIA_IDS_MIN) || 11362306a36Sopenharmony_ci (*nr >= KVM_DEV_RISCV_AIA_IDS_MAX) || 11462306a36Sopenharmony_ci ((*nr & KVM_DEV_RISCV_AIA_IDS_MIN) != 11562306a36Sopenharmony_ci KVM_DEV_RISCV_AIA_IDS_MIN) || 11662306a36Sopenharmony_ci (kvm_riscv_aia_max_ids <= *nr)) 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci aia->nr_ids = *nr; 11962306a36Sopenharmony_ci } else 12062306a36Sopenharmony_ci *nr = aia->nr_ids; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_SRCS: 12362306a36Sopenharmony_ci if (write) { 12462306a36Sopenharmony_ci if ((*nr >= KVM_DEV_RISCV_AIA_SRCS_MAX) || 12562306a36Sopenharmony_ci (*nr >= kvm_riscv_aia_max_ids)) 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci aia->nr_sources = *nr; 12862306a36Sopenharmony_ci } else 12962306a36Sopenharmony_ci *nr = aia->nr_sources; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS: 13262306a36Sopenharmony_ci if (write) { 13362306a36Sopenharmony_ci if (*nr >= KVM_DEV_RISCV_AIA_GROUP_BITS_MAX) 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci aia->nr_group_bits = *nr; 13662306a36Sopenharmony_ci } else 13762306a36Sopenharmony_ci *nr = aia->nr_group_bits; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT: 14062306a36Sopenharmony_ci if (write) { 14162306a36Sopenharmony_ci if ((*nr < KVM_DEV_RISCV_AIA_GROUP_SHIFT_MIN) || 14262306a36Sopenharmony_ci (*nr >= KVM_DEV_RISCV_AIA_GROUP_SHIFT_MAX)) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci aia->nr_group_shift = *nr; 14562306a36Sopenharmony_ci } else 14662306a36Sopenharmony_ci *nr = aia->nr_group_shift; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_HART_BITS: 14962306a36Sopenharmony_ci if (write) { 15062306a36Sopenharmony_ci if (*nr >= KVM_DEV_RISCV_AIA_HART_BITS_MAX) 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci aia->nr_hart_bits = *nr; 15362306a36Sopenharmony_ci } else 15462306a36Sopenharmony_ci *nr = aia->nr_hart_bits; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS: 15762306a36Sopenharmony_ci if (write) { 15862306a36Sopenharmony_ci if (*nr >= KVM_DEV_RISCV_AIA_GUEST_BITS_MAX) 15962306a36Sopenharmony_ci return -EINVAL; 16062306a36Sopenharmony_ci aia->nr_guest_bits = *nr; 16162306a36Sopenharmony_ci } else 16262306a36Sopenharmony_ci *nr = aia->nr_guest_bits; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci default: 16562306a36Sopenharmony_ci return -ENXIO; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int aia_aplic_addr(struct kvm *kvm, u64 *addr, bool write) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct kvm_aia *aia = &kvm->arch.aia; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (write) { 17662306a36Sopenharmony_ci /* Writes can only be done before irqchip is initialized */ 17762306a36Sopenharmony_ci if (kvm_riscv_aia_initialized(kvm)) 17862306a36Sopenharmony_ci return -EBUSY; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (*addr & (KVM_DEV_RISCV_APLIC_ALIGN - 1)) 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci aia->aplic_addr = *addr; 18462306a36Sopenharmony_ci } else 18562306a36Sopenharmony_ci *addr = aia->aplic_addr; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int aia_imsic_addr(struct kvm *kvm, u64 *addr, 19162306a36Sopenharmony_ci unsigned long vcpu_idx, bool write) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 19462306a36Sopenharmony_ci struct kvm_vcpu_aia *vcpu_aia; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, vcpu_idx); 19762306a36Sopenharmony_ci if (!vcpu) 19862306a36Sopenharmony_ci return -EINVAL; 19962306a36Sopenharmony_ci vcpu_aia = &vcpu->arch.aia_context; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (write) { 20262306a36Sopenharmony_ci /* Writes can only be done before irqchip is initialized */ 20362306a36Sopenharmony_ci if (kvm_riscv_aia_initialized(kvm)) 20462306a36Sopenharmony_ci return -EBUSY; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (*addr & (KVM_DEV_RISCV_IMSIC_ALIGN - 1)) 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci mutex_lock(&vcpu->mutex); 21162306a36Sopenharmony_ci if (write) 21262306a36Sopenharmony_ci vcpu_aia->imsic_addr = *addr; 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci *addr = vcpu_aia->imsic_addr; 21562306a36Sopenharmony_ci mutex_unlock(&vcpu->mutex); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic gpa_t aia_imsic_ppn(struct kvm_aia *aia, gpa_t addr) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci u32 h, l; 22362306a36Sopenharmony_ci gpa_t mask = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci h = aia->nr_hart_bits + aia->nr_guest_bits + 22662306a36Sopenharmony_ci IMSIC_MMIO_PAGE_SHIFT - 1; 22762306a36Sopenharmony_ci mask = GENMASK_ULL(h, 0); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (aia->nr_group_bits) { 23062306a36Sopenharmony_ci h = aia->nr_group_bits + aia->nr_group_shift - 1; 23162306a36Sopenharmony_ci l = aia->nr_group_shift; 23262306a36Sopenharmony_ci mask |= GENMASK_ULL(h, l); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return (addr & ~mask) >> IMSIC_MMIO_PAGE_SHIFT; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic u32 aia_imsic_hart_index(struct kvm_aia *aia, gpa_t addr) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci u32 hart, group = 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci hart = (addr >> (aia->nr_guest_bits + IMSIC_MMIO_PAGE_SHIFT)) & 24362306a36Sopenharmony_ci GENMASK_ULL(aia->nr_hart_bits - 1, 0); 24462306a36Sopenharmony_ci if (aia->nr_group_bits) 24562306a36Sopenharmony_ci group = (addr >> aia->nr_group_shift) & 24662306a36Sopenharmony_ci GENMASK_ULL(aia->nr_group_bits - 1, 0); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return (group << aia->nr_hart_bits) | hart; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int aia_init(struct kvm *kvm) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int ret, i; 25462306a36Sopenharmony_ci unsigned long idx; 25562306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 25662306a36Sopenharmony_ci struct kvm_vcpu_aia *vaia; 25762306a36Sopenharmony_ci struct kvm_aia *aia = &kvm->arch.aia; 25862306a36Sopenharmony_ci gpa_t base_ppn = KVM_RISCV_AIA_UNDEF_ADDR; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Irqchip can be initialized only once */ 26162306a36Sopenharmony_ci if (kvm_riscv_aia_initialized(kvm)) 26262306a36Sopenharmony_ci return -EBUSY; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* We might be in the middle of creating a VCPU? */ 26562306a36Sopenharmony_ci if (kvm->created_vcpus != atomic_read(&kvm->online_vcpus)) 26662306a36Sopenharmony_ci return -EBUSY; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Number of sources should be less than or equals number of IDs */ 26962306a36Sopenharmony_ci if (aia->nr_ids < aia->nr_sources) 27062306a36Sopenharmony_ci return -EINVAL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* APLIC base is required for non-zero number of sources */ 27362306a36Sopenharmony_ci if (aia->nr_sources && aia->aplic_addr == KVM_RISCV_AIA_UNDEF_ADDR) 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Initialize APLIC */ 27762306a36Sopenharmony_ci ret = kvm_riscv_aia_aplic_init(kvm); 27862306a36Sopenharmony_ci if (ret) 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Iterate over each VCPU */ 28262306a36Sopenharmony_ci kvm_for_each_vcpu(idx, vcpu, kvm) { 28362306a36Sopenharmony_ci vaia = &vcpu->arch.aia_context; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* IMSIC base is required */ 28662306a36Sopenharmony_ci if (vaia->imsic_addr == KVM_RISCV_AIA_UNDEF_ADDR) { 28762306a36Sopenharmony_ci ret = -EINVAL; 28862306a36Sopenharmony_ci goto fail_cleanup_imsics; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* All IMSICs should have matching base PPN */ 29262306a36Sopenharmony_ci if (base_ppn == KVM_RISCV_AIA_UNDEF_ADDR) 29362306a36Sopenharmony_ci base_ppn = aia_imsic_ppn(aia, vaia->imsic_addr); 29462306a36Sopenharmony_ci if (base_ppn != aia_imsic_ppn(aia, vaia->imsic_addr)) { 29562306a36Sopenharmony_ci ret = -EINVAL; 29662306a36Sopenharmony_ci goto fail_cleanup_imsics; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Update HART index of the IMSIC based on IMSIC base */ 30062306a36Sopenharmony_ci vaia->hart_index = aia_imsic_hart_index(aia, 30162306a36Sopenharmony_ci vaia->imsic_addr); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Initialize IMSIC for this VCPU */ 30462306a36Sopenharmony_ci ret = kvm_riscv_vcpu_aia_imsic_init(vcpu); 30562306a36Sopenharmony_ci if (ret) 30662306a36Sopenharmony_ci goto fail_cleanup_imsics; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Set the initialized flag */ 31062306a36Sopenharmony_ci kvm->arch.aia.initialized = true; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cifail_cleanup_imsics: 31562306a36Sopenharmony_ci for (i = idx - 1; i >= 0; i--) { 31662306a36Sopenharmony_ci vcpu = kvm_get_vcpu(kvm, i); 31762306a36Sopenharmony_ci if (!vcpu) 31862306a36Sopenharmony_ci continue; 31962306a36Sopenharmony_ci kvm_riscv_vcpu_aia_imsic_cleanup(vcpu); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci kvm_riscv_aia_aplic_cleanup(kvm); 32262306a36Sopenharmony_ci return ret; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int aia_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci u32 nr; 32862306a36Sopenharmony_ci u64 addr; 32962306a36Sopenharmony_ci int nr_vcpus, r = -ENXIO; 33062306a36Sopenharmony_ci unsigned long v, type = (unsigned long)attr->attr; 33162306a36Sopenharmony_ci void __user *uaddr = (void __user *)(long)attr->addr; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci switch (attr->group) { 33462306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_CONFIG: 33562306a36Sopenharmony_ci if (copy_from_user(&nr, uaddr, sizeof(nr))) 33662306a36Sopenharmony_ci return -EFAULT; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 33962306a36Sopenharmony_ci r = aia_config(dev->kvm, type, &nr, true); 34062306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_ADDR: 34562306a36Sopenharmony_ci if (copy_from_user(&addr, uaddr, sizeof(addr))) 34662306a36Sopenharmony_ci return -EFAULT; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci nr_vcpus = atomic_read(&dev->kvm->online_vcpus); 34962306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 35062306a36Sopenharmony_ci if (type == KVM_DEV_RISCV_AIA_ADDR_APLIC) 35162306a36Sopenharmony_ci r = aia_aplic_addr(dev->kvm, &addr, true); 35262306a36Sopenharmony_ci else if (type < KVM_DEV_RISCV_AIA_ADDR_IMSIC(nr_vcpus)) 35362306a36Sopenharmony_ci r = aia_imsic_addr(dev->kvm, &addr, 35462306a36Sopenharmony_ci type - KVM_DEV_RISCV_AIA_ADDR_IMSIC(0), true); 35562306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_CTRL: 36062306a36Sopenharmony_ci switch (type) { 36162306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CTRL_INIT: 36262306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 36362306a36Sopenharmony_ci r = aia_init(dev->kvm); 36462306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_APLIC: 37062306a36Sopenharmony_ci if (copy_from_user(&nr, uaddr, sizeof(nr))) 37162306a36Sopenharmony_ci return -EFAULT; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 37462306a36Sopenharmony_ci r = kvm_riscv_aia_aplic_set_attr(dev->kvm, type, nr); 37562306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_IMSIC: 37962306a36Sopenharmony_ci if (copy_from_user(&v, uaddr, sizeof(v))) 38062306a36Sopenharmony_ci return -EFAULT; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 38362306a36Sopenharmony_ci r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, true, &v); 38462306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return r; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int aia_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci u32 nr; 39562306a36Sopenharmony_ci u64 addr; 39662306a36Sopenharmony_ci int nr_vcpus, r = -ENXIO; 39762306a36Sopenharmony_ci void __user *uaddr = (void __user *)(long)attr->addr; 39862306a36Sopenharmony_ci unsigned long v, type = (unsigned long)attr->attr; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci switch (attr->group) { 40162306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_CONFIG: 40262306a36Sopenharmony_ci if (copy_from_user(&nr, uaddr, sizeof(nr))) 40362306a36Sopenharmony_ci return -EFAULT; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 40662306a36Sopenharmony_ci r = aia_config(dev->kvm, type, &nr, false); 40762306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 40862306a36Sopenharmony_ci if (r) 40962306a36Sopenharmony_ci return r; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (copy_to_user(uaddr, &nr, sizeof(nr))) 41262306a36Sopenharmony_ci return -EFAULT; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_ADDR: 41662306a36Sopenharmony_ci if (copy_from_user(&addr, uaddr, sizeof(addr))) 41762306a36Sopenharmony_ci return -EFAULT; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci nr_vcpus = atomic_read(&dev->kvm->online_vcpus); 42062306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 42162306a36Sopenharmony_ci if (type == KVM_DEV_RISCV_AIA_ADDR_APLIC) 42262306a36Sopenharmony_ci r = aia_aplic_addr(dev->kvm, &addr, false); 42362306a36Sopenharmony_ci else if (type < KVM_DEV_RISCV_AIA_ADDR_IMSIC(nr_vcpus)) 42462306a36Sopenharmony_ci r = aia_imsic_addr(dev->kvm, &addr, 42562306a36Sopenharmony_ci type - KVM_DEV_RISCV_AIA_ADDR_IMSIC(0), false); 42662306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 42762306a36Sopenharmony_ci if (r) 42862306a36Sopenharmony_ci return r; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (copy_to_user(uaddr, &addr, sizeof(addr))) 43162306a36Sopenharmony_ci return -EFAULT; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_APLIC: 43562306a36Sopenharmony_ci if (copy_from_user(&nr, uaddr, sizeof(nr))) 43662306a36Sopenharmony_ci return -EFAULT; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 43962306a36Sopenharmony_ci r = kvm_riscv_aia_aplic_get_attr(dev->kvm, type, &nr); 44062306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 44162306a36Sopenharmony_ci if (r) 44262306a36Sopenharmony_ci return r; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (copy_to_user(uaddr, &nr, sizeof(nr))) 44562306a36Sopenharmony_ci return -EFAULT; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_IMSIC: 44962306a36Sopenharmony_ci if (copy_from_user(&v, uaddr, sizeof(v))) 45062306a36Sopenharmony_ci return -EFAULT; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci mutex_lock(&dev->kvm->lock); 45362306a36Sopenharmony_ci r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, false, &v); 45462306a36Sopenharmony_ci mutex_unlock(&dev->kvm->lock); 45562306a36Sopenharmony_ci if (r) 45662306a36Sopenharmony_ci return r; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (copy_to_user(uaddr, &v, sizeof(v))) 45962306a36Sopenharmony_ci return -EFAULT; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return r; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int aia_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int nr_vcpus; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci switch (attr->group) { 47262306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_CONFIG: 47362306a36Sopenharmony_ci switch (attr->attr) { 47462306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_MODE: 47562306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_IDS: 47662306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_SRCS: 47762306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS: 47862306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT: 47962306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_HART_BITS: 48062306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS: 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_ADDR: 48562306a36Sopenharmony_ci nr_vcpus = atomic_read(&dev->kvm->online_vcpus); 48662306a36Sopenharmony_ci if (attr->attr == KVM_DEV_RISCV_AIA_ADDR_APLIC) 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci else if (attr->attr < KVM_DEV_RISCV_AIA_ADDR_IMSIC(nr_vcpus)) 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_CTRL: 49262306a36Sopenharmony_ci switch (attr->attr) { 49362306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_CTRL_INIT: 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_APLIC: 49862306a36Sopenharmony_ci return kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr); 49962306a36Sopenharmony_ci case KVM_DEV_RISCV_AIA_GRP_IMSIC: 50062306a36Sopenharmony_ci return kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return -ENXIO; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistruct kvm_device_ops kvm_riscv_aia_device_ops = { 50762306a36Sopenharmony_ci .name = "kvm-riscv-aia", 50862306a36Sopenharmony_ci .create = aia_create, 50962306a36Sopenharmony_ci .destroy = aia_destroy, 51062306a36Sopenharmony_ci .set_attr = aia_set_attr, 51162306a36Sopenharmony_ci .get_attr = aia_get_attr, 51262306a36Sopenharmony_ci .has_attr = aia_has_attr, 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_update(struct kvm_vcpu *vcpu) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci /* Proceed only if AIA was initialized successfully */ 51862306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(vcpu->kvm)) 51962306a36Sopenharmony_ci return 1; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* Update the IMSIC HW state before entering guest mode */ 52262306a36Sopenharmony_ci return kvm_riscv_vcpu_aia_imsic_update(vcpu); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_reset(struct kvm_vcpu *vcpu) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; 52862306a36Sopenharmony_ci struct kvm_vcpu_aia_csr *reset_csr = 52962306a36Sopenharmony_ci &vcpu->arch.aia_context.guest_reset_csr; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci memcpy(csr, reset_csr, sizeof(*csr)); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Proceed only if AIA was initialized successfully */ 53662306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(vcpu->kvm)) 53762306a36Sopenharmony_ci return; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Reset the IMSIC context */ 54062306a36Sopenharmony_ci kvm_riscv_vcpu_aia_imsic_reset(vcpu); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciint kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct kvm_vcpu_aia *vaia = &vcpu->arch.aia_context; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * We don't do any memory allocations over here because these 55262306a36Sopenharmony_ci * will be done after AIA device is initialized by the user-space. 55362306a36Sopenharmony_ci * 55462306a36Sopenharmony_ci * Refer, aia_init() implementation for more details. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Initialize default values in AIA vcpu context */ 55862306a36Sopenharmony_ci vaia->imsic_addr = KVM_RISCV_AIA_UNDEF_ADDR; 55962306a36Sopenharmony_ci vaia->hart_index = vcpu->vcpu_idx; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_civoid kvm_riscv_vcpu_aia_deinit(struct kvm_vcpu *vcpu) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci /* Proceed only if AIA was initialized successfully */ 56762306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(vcpu->kvm)) 56862306a36Sopenharmony_ci return; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Cleanup IMSIC context */ 57162306a36Sopenharmony_ci kvm_riscv_vcpu_aia_imsic_cleanup(vcpu); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ciint kvm_riscv_aia_inject_msi_by_id(struct kvm *kvm, u32 hart_index, 57562306a36Sopenharmony_ci u32 guest_index, u32 iid) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci unsigned long idx; 57862306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Proceed only if AIA was initialized successfully */ 58162306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(kvm)) 58262306a36Sopenharmony_ci return -EBUSY; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Inject MSI to matching VCPU */ 58562306a36Sopenharmony_ci kvm_for_each_vcpu(idx, vcpu, kvm) { 58662306a36Sopenharmony_ci if (vcpu->arch.aia_context.hart_index == hart_index) 58762306a36Sopenharmony_ci return kvm_riscv_vcpu_aia_imsic_inject(vcpu, 58862306a36Sopenharmony_ci guest_index, 58962306a36Sopenharmony_ci 0, iid); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ciint kvm_riscv_aia_inject_msi(struct kvm *kvm, struct kvm_msi *msi) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci gpa_t tppn, ippn; 59862306a36Sopenharmony_ci unsigned long idx; 59962306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 60062306a36Sopenharmony_ci u32 g, toff, iid = msi->data; 60162306a36Sopenharmony_ci struct kvm_aia *aia = &kvm->arch.aia; 60262306a36Sopenharmony_ci gpa_t target = (((gpa_t)msi->address_hi) << 32) | msi->address_lo; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* Proceed only if AIA was initialized successfully */ 60562306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(kvm)) 60662306a36Sopenharmony_ci return -EBUSY; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Convert target address to target PPN */ 60962306a36Sopenharmony_ci tppn = target >> IMSIC_MMIO_PAGE_SHIFT; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Extract and clear Guest ID from target PPN */ 61262306a36Sopenharmony_ci g = tppn & (BIT(aia->nr_guest_bits) - 1); 61362306a36Sopenharmony_ci tppn &= ~((gpa_t)(BIT(aia->nr_guest_bits) - 1)); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Inject MSI to matching VCPU */ 61662306a36Sopenharmony_ci kvm_for_each_vcpu(idx, vcpu, kvm) { 61762306a36Sopenharmony_ci ippn = vcpu->arch.aia_context.imsic_addr >> 61862306a36Sopenharmony_ci IMSIC_MMIO_PAGE_SHIFT; 61962306a36Sopenharmony_ci if (ippn == tppn) { 62062306a36Sopenharmony_ci toff = target & (IMSIC_MMIO_PAGE_SZ - 1); 62162306a36Sopenharmony_ci return kvm_riscv_vcpu_aia_imsic_inject(vcpu, g, 62262306a36Sopenharmony_ci toff, iid); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return 0; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ciint kvm_riscv_aia_inject_irq(struct kvm *kvm, unsigned int irq, bool level) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci /* Proceed only if AIA was initialized successfully */ 63262306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(kvm)) 63362306a36Sopenharmony_ci return -EBUSY; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Inject interrupt level change in APLIC */ 63662306a36Sopenharmony_ci return kvm_riscv_aia_aplic_inject(kvm, irq, level); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_civoid kvm_riscv_aia_init_vm(struct kvm *kvm) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct kvm_aia *aia = &kvm->arch.aia; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (!kvm_riscv_aia_available()) 64462306a36Sopenharmony_ci return; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * We don't do any memory allocations over here because these 64862306a36Sopenharmony_ci * will be done after AIA device is initialized by the user-space. 64962306a36Sopenharmony_ci * 65062306a36Sopenharmony_ci * Refer, aia_init() implementation for more details. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Initialize default values in AIA global context */ 65462306a36Sopenharmony_ci aia->mode = (kvm_riscv_aia_nr_hgei) ? 65562306a36Sopenharmony_ci KVM_DEV_RISCV_AIA_MODE_AUTO : KVM_DEV_RISCV_AIA_MODE_EMUL; 65662306a36Sopenharmony_ci aia->nr_ids = kvm_riscv_aia_max_ids - 1; 65762306a36Sopenharmony_ci aia->nr_sources = 0; 65862306a36Sopenharmony_ci aia->nr_group_bits = 0; 65962306a36Sopenharmony_ci aia->nr_group_shift = KVM_DEV_RISCV_AIA_GROUP_SHIFT_MIN; 66062306a36Sopenharmony_ci aia->nr_hart_bits = 0; 66162306a36Sopenharmony_ci aia->nr_guest_bits = 0; 66262306a36Sopenharmony_ci aia->aplic_addr = KVM_RISCV_AIA_UNDEF_ADDR; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_civoid kvm_riscv_aia_destroy_vm(struct kvm *kvm) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci /* Proceed only if AIA was initialized successfully */ 66862306a36Sopenharmony_ci if (!kvm_riscv_aia_initialized(kvm)) 66962306a36Sopenharmony_ci return; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Cleanup APLIC context */ 67262306a36Sopenharmony_ci kvm_riscv_aia_aplic_cleanup(kvm); 67362306a36Sopenharmony_ci} 674