Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Anup Patel | 2968 | 99.50% | 12 | 80.00% |
Yong-Xuan Wang | 8 | 0.27% | 1 | 6.67% |
Nikolay Borisov | 4 | 0.13% | 1 | 6.67% |
Atish Patra | 3 | 0.10% | 1 | 6.67% |
Total | 2983 | 15 |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 Western Digital Corporation or its affiliates. * Copyright (C) 2022 Ventana Micro Systems Inc. * * Authors: * Anup Patel <apatel@ventanamicro.com> */ #include <linux/bits.h> #include <linux/irqchip/riscv-imsic.h> #include <linux/kvm_host.h> #include <linux/uaccess.h> static void unlock_vcpus(struct kvm *kvm, int vcpu_lock_idx) { struct kvm_vcpu *tmp_vcpu; for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) { tmp_vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx); mutex_unlock(&tmp_vcpu->mutex); } } static void unlock_all_vcpus(struct kvm *kvm) { unlock_vcpus(kvm, atomic_read(&kvm->online_vcpus) - 1); } static bool lock_all_vcpus(struct kvm *kvm) { struct kvm_vcpu *tmp_vcpu; unsigned long c; kvm_for_each_vcpu(c, tmp_vcpu, kvm) { if (!mutex_trylock(&tmp_vcpu->mutex)) { unlock_vcpus(kvm, c - 1); return false; } } return true; } static int aia_create(struct kvm_device *dev, u32 type) { int ret; unsigned long i; struct kvm *kvm = dev->kvm; struct kvm_vcpu *vcpu; if (irqchip_in_kernel(kvm)) return -EEXIST; ret = -EBUSY; if (!lock_all_vcpus(kvm)) return ret; kvm_for_each_vcpu(i, vcpu, kvm) { if (vcpu->arch.ran_atleast_once) goto out_unlock; } ret = 0; kvm->arch.aia.in_kernel = true; out_unlock: unlock_all_vcpus(kvm); return ret; } static void aia_destroy(struct kvm_device *dev) { kfree(dev); } static int aia_config(struct kvm *kvm, unsigned long type, u32 *nr, bool write) { struct kvm_aia *aia = &kvm->arch.aia; /* Writes can only be done before irqchip is initialized */ if (write && kvm_riscv_aia_initialized(kvm)) return -EBUSY; switch (type) { case KVM_DEV_RISCV_AIA_CONFIG_MODE: if (write) { switch (*nr) { case KVM_DEV_RISCV_AIA_MODE_EMUL: break; case KVM_DEV_RISCV_AIA_MODE_HWACCEL: case KVM_DEV_RISCV_AIA_MODE_AUTO: /* * HW Acceleration and Auto modes only * supported on host with non-zero guest * external interrupts (i.e. non-zero * VS-level IMSIC pages). */ if (!kvm_riscv_aia_nr_hgei) return -EINVAL; break; default: return -EINVAL; } aia->mode = *nr; } else *nr = aia->mode; break; case KVM_DEV_RISCV_AIA_CONFIG_IDS: if (write) { if ((*nr < KVM_DEV_RISCV_AIA_IDS_MIN) || (*nr >= KVM_DEV_RISCV_AIA_IDS_MAX) || ((*nr & KVM_DEV_RISCV_AIA_IDS_MIN) != KVM_DEV_RISCV_AIA_IDS_MIN) || (kvm_riscv_aia_max_ids <= *nr)) return -EINVAL; aia->nr_ids = *nr; } else *nr = aia->nr_ids; break; case KVM_DEV_RISCV_AIA_CONFIG_SRCS: if (write) { if ((*nr >= KVM_DEV_RISCV_AIA_SRCS_MAX) || (*nr >= kvm_riscv_aia_max_ids)) return -EINVAL; aia->nr_sources = *nr; } else *nr = aia->nr_sources; break; case KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS: if (write) { if (*nr >= KVM_DEV_RISCV_AIA_GROUP_BITS_MAX) return -EINVAL; aia->nr_group_bits = *nr; } else *nr = aia->nr_group_bits; break; case KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT: if (write) { if ((*nr < KVM_DEV_RISCV_AIA_GROUP_SHIFT_MIN) || (*nr >= KVM_DEV_RISCV_AIA_GROUP_SHIFT_MAX)) return -EINVAL; aia->nr_group_shift = *nr; } else *nr = aia->nr_group_shift; break; case KVM_DEV_RISCV_AIA_CONFIG_HART_BITS: if (write) { if (*nr >= KVM_DEV_RISCV_AIA_HART_BITS_MAX) return -EINVAL; aia->nr_hart_bits = *nr; } else *nr = aia->nr_hart_bits; break; case KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS: if (write) { if (*nr >= KVM_DEV_RISCV_AIA_GUEST_BITS_MAX) return -EINVAL; aia->nr_guest_bits = *nr; } else *nr = aia->nr_guest_bits; break; default: return -ENXIO; } return 0; } static int aia_aplic_addr(struct kvm *kvm, u64 *addr, bool write) { struct kvm_aia *aia = &kvm->arch.aia; if (write) { /* Writes can only be done before irqchip is initialized */ if (kvm_riscv_aia_initialized(kvm)) return -EBUSY; if (*addr & (KVM_DEV_RISCV_APLIC_ALIGN - 1)) return -EINVAL; aia->aplic_addr = *addr; } else *addr = aia->aplic_addr; return 0; } static int aia_imsic_addr(struct kvm *kvm, u64 *addr, unsigned long vcpu_idx, bool write) { struct kvm_vcpu *vcpu; struct kvm_vcpu_aia *vcpu_aia; vcpu = kvm_get_vcpu(kvm, vcpu_idx); if (!vcpu) return -EINVAL; vcpu_aia = &vcpu->arch.aia_context; if (write) { /* Writes can only be done before irqchip is initialized */ if (kvm_riscv_aia_initialized(kvm)) return -EBUSY; if (*addr & (KVM_DEV_RISCV_IMSIC_ALIGN - 1)) return -EINVAL; } mutex_lock(&vcpu->mutex); if (write) vcpu_aia->imsic_addr = *addr; else *addr = vcpu_aia->imsic_addr; mutex_unlock(&vcpu->mutex); return 0; } static gpa_t aia_imsic_ppn(struct kvm_aia *aia, gpa_t addr) { u32 h, l; gpa_t mask = 0; h = aia->nr_hart_bits + aia->nr_guest_bits + IMSIC_MMIO_PAGE_SHIFT - 1; mask = GENMASK_ULL(h, 0); if (aia->nr_group_bits) { h = aia->nr_group_bits + aia->nr_group_shift - 1; l = aia->nr_group_shift; mask |= GENMASK_ULL(h, l); } return (addr & ~mask) >> IMSIC_MMIO_PAGE_SHIFT; } static u32 aia_imsic_hart_index(struct kvm_aia *aia, gpa_t addr) { u32 hart = 0, group = 0; if (aia->nr_hart_bits) hart = (addr >> (aia->nr_guest_bits + IMSIC_MMIO_PAGE_SHIFT)) & GENMASK_ULL(aia->nr_hart_bits - 1, 0); if (aia->nr_group_bits) group = (addr >> aia->nr_group_shift) & GENMASK_ULL(aia->nr_group_bits - 1, 0); return (group << aia->nr_hart_bits) | hart; } static int aia_init(struct kvm *kvm) { int ret, i; unsigned long idx; struct kvm_vcpu *vcpu; struct kvm_vcpu_aia *vaia; struct kvm_aia *aia = &kvm->arch.aia; gpa_t base_ppn = KVM_RISCV_AIA_UNDEF_ADDR; /* Irqchip can be initialized only once */ if (kvm_riscv_aia_initialized(kvm)) return -EBUSY; /* We might be in the middle of creating a VCPU? */ if (kvm->created_vcpus != atomic_read(&kvm->online_vcpus)) return -EBUSY; /* Number of sources should be less than or equals number of IDs */ if (aia->nr_ids < aia->nr_sources) return -EINVAL; /* APLIC base is required for non-zero number of sources */ if (aia->nr_sources && aia->aplic_addr == KVM_RISCV_AIA_UNDEF_ADDR) return -EINVAL; /* Initialize APLIC */ ret = kvm_riscv_aia_aplic_init(kvm); if (ret) return ret; /* Iterate over each VCPU */ kvm_for_each_vcpu(idx, vcpu, kvm) { vaia = &vcpu->arch.aia_context; /* IMSIC base is required */ if (vaia->imsic_addr == KVM_RISCV_AIA_UNDEF_ADDR) { ret = -EINVAL; goto fail_cleanup_imsics; } /* All IMSICs should have matching base PPN */ if (base_ppn == KVM_RISCV_AIA_UNDEF_ADDR) base_ppn = aia_imsic_ppn(aia, vaia->imsic_addr); if (base_ppn != aia_imsic_ppn(aia, vaia->imsic_addr)) { ret = -EINVAL; goto fail_cleanup_imsics; } /* Update HART index of the IMSIC based on IMSIC base */ vaia->hart_index = aia_imsic_hart_index(aia, vaia->imsic_addr); /* Initialize IMSIC for this VCPU */ ret = kvm_riscv_vcpu_aia_imsic_init(vcpu); if (ret) goto fail_cleanup_imsics; } /* Set the initialized flag */ kvm->arch.aia.initialized = true; return 0; fail_cleanup_imsics: for (i = idx - 1; i >= 0; i--) { vcpu = kvm_get_vcpu(kvm, i); if (!vcpu) continue; kvm_riscv_vcpu_aia_imsic_cleanup(vcpu); } kvm_riscv_aia_aplic_cleanup(kvm); return ret; } static int aia_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { u32 nr; u64 addr; int nr_vcpus, r = -ENXIO; unsigned long v, type = (unsigned long)attr->attr; void __user *uaddr = (void __user *)(long)attr->addr; switch (attr->group) { case KVM_DEV_RISCV_AIA_GRP_CONFIG: if (copy_from_user(&nr, uaddr, sizeof(nr))) return -EFAULT; mutex_lock(&dev->kvm->lock); r = aia_config(dev->kvm, type, &nr, true); mutex_unlock(&dev->kvm->lock); break; case KVM_DEV_RISCV_AIA_GRP_ADDR: if (copy_from_user(&addr, uaddr, sizeof(addr))) return -EFAULT; nr_vcpus = atomic_read(&dev->kvm->online_vcpus); mutex_lock(&dev->kvm->lock); if (type == KVM_DEV_RISCV_AIA_ADDR_APLIC) r = aia_aplic_addr(dev->kvm, &addr, true); else if (type < KVM_DEV_RISCV_AIA_ADDR_IMSIC(nr_vcpus)) r = aia_imsic_addr(dev->kvm, &addr, type - KVM_DEV_RISCV_AIA_ADDR_IMSIC(0), true); mutex_unlock(&dev->kvm->lock); break; case KVM_DEV_RISCV_AIA_GRP_CTRL: switch (type) { case KVM_DEV_RISCV_AIA_CTRL_INIT: mutex_lock(&dev->kvm->lock); r = aia_init(dev->kvm); mutex_unlock(&dev->kvm->lock); break; } break; case KVM_DEV_RISCV_AIA_GRP_APLIC: if (copy_from_user(&nr, uaddr, sizeof(nr))) return -EFAULT; mutex_lock(&dev->kvm->lock); r = kvm_riscv_aia_aplic_set_attr(dev->kvm, type, nr); mutex_unlock(&dev->kvm->lock); break; case KVM_DEV_RISCV_AIA_GRP_IMSIC: if (copy_from_user(&v, uaddr, sizeof(v))) return -EFAULT; mutex_lock(&dev->kvm->lock); r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, true, &v); mutex_unlock(&dev->kvm->lock); break; } return r; } static int aia_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { u32 nr; u64 addr; int nr_vcpus, r = -ENXIO; void __user *uaddr = (void __user *)(long)attr->addr; unsigned long v, type = (unsigned long)attr->attr; switch (attr->group) { case KVM_DEV_RISCV_AIA_GRP_CONFIG: if (copy_from_user(&nr, uaddr, sizeof(nr))) return -EFAULT; mutex_lock(&dev->kvm->lock); r = aia_config(dev->kvm, type, &nr, false); mutex_unlock(&dev->kvm->lock); if (r) return r; if (copy_to_user(uaddr, &nr, sizeof(nr))) return -EFAULT; break; case KVM_DEV_RISCV_AIA_GRP_ADDR: if (copy_from_user(&addr, uaddr, sizeof(addr))) return -EFAULT; nr_vcpus = atomic_read(&dev->kvm->online_vcpus); mutex_lock(&dev->kvm->lock); if (type == KVM_DEV_RISCV_AIA_ADDR_APLIC) r = aia_aplic_addr(dev->kvm, &addr, false); else if (type < KVM_DEV_RISCV_AIA_ADDR_IMSIC(nr_vcpus)) r = aia_imsic_addr(dev->kvm, &addr, type - KVM_DEV_RISCV_AIA_ADDR_IMSIC(0), false); mutex_unlock(&dev->kvm->lock); if (r) return r; if (copy_to_user(uaddr, &addr, sizeof(addr))) return -EFAULT; break; case KVM_DEV_RISCV_AIA_GRP_APLIC: if (copy_from_user(&nr, uaddr, sizeof(nr))) return -EFAULT; mutex_lock(&dev->kvm->lock); r = kvm_riscv_aia_aplic_get_attr(dev->kvm, type, &nr); mutex_unlock(&dev->kvm->lock); if (r) return r; if (copy_to_user(uaddr, &nr, sizeof(nr))) return -EFAULT; break; case KVM_DEV_RISCV_AIA_GRP_IMSIC: if (copy_from_user(&v, uaddr, sizeof(v))) return -EFAULT; mutex_lock(&dev->kvm->lock); r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, false, &v); mutex_unlock(&dev->kvm->lock); if (r) return r; if (copy_to_user(uaddr, &v, sizeof(v))) return -EFAULT; break; } return r; } static int aia_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { int nr_vcpus; switch (attr->group) { case KVM_DEV_RISCV_AIA_GRP_CONFIG: switch (attr->attr) { case KVM_DEV_RISCV_AIA_CONFIG_MODE: case KVM_DEV_RISCV_AIA_CONFIG_IDS: case KVM_DEV_RISCV_AIA_CONFIG_SRCS: case KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS: case KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT: case KVM_DEV_RISCV_AIA_CONFIG_HART_BITS: case KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS: return 0; } break; case KVM_DEV_RISCV_AIA_GRP_ADDR: nr_vcpus = atomic_read(&dev->kvm->online_vcpus); if (attr->attr == KVM_DEV_RISCV_AIA_ADDR_APLIC) return 0; else if (attr->attr < KVM_DEV_RISCV_AIA_ADDR_IMSIC(nr_vcpus)) return 0; break; case KVM_DEV_RISCV_AIA_GRP_CTRL: switch (attr->attr) { case KVM_DEV_RISCV_AIA_CTRL_INIT: return 0; } break; case KVM_DEV_RISCV_AIA_GRP_APLIC: return kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr); case KVM_DEV_RISCV_AIA_GRP_IMSIC: return kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr); } return -ENXIO; } struct kvm_device_ops kvm_riscv_aia_device_ops = { .name = "kvm-riscv-aia", .create = aia_create, .destroy = aia_destroy, .set_attr = aia_set_attr, .get_attr = aia_get_attr, .has_attr = aia_has_attr, }; int kvm_riscv_vcpu_aia_update(struct kvm_vcpu *vcpu) { /* Proceed only if AIA was initialized successfully */ if (!kvm_riscv_aia_initialized(vcpu->kvm)) return 1; /* Update the IMSIC HW state before entering guest mode */ return kvm_riscv_vcpu_aia_imsic_update(vcpu); } void kvm_riscv_vcpu_aia_reset(struct kvm_vcpu *vcpu) { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; struct kvm_vcpu_aia_csr *reset_csr = &vcpu->arch.aia_context.guest_reset_csr; if (!kvm_riscv_aia_available()) return; memcpy(csr, reset_csr, sizeof(*csr)); /* Proceed only if AIA was initialized successfully */ if (!kvm_riscv_aia_initialized(vcpu->kvm)) return; /* Reset the IMSIC context */ kvm_riscv_vcpu_aia_imsic_reset(vcpu); } int kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu) { struct kvm_vcpu_aia *vaia = &vcpu->arch.aia_context; if (!kvm_riscv_aia_available()) return 0; /* * We don't do any memory allocations over here because these * will be done after AIA device is initialized by the user-space. * * Refer, aia_init() implementation for more details. */ /* Initialize default values in AIA vcpu context */ vaia->imsic_addr = KVM_RISCV_AIA_UNDEF_ADDR; vaia->hart_index = vcpu->vcpu_idx; return 0; } void kvm_riscv_vcpu_aia_deinit(struct kvm_vcpu *vcpu) { /* Proceed only if AIA was initialized successfully */ if (!kvm_riscv_aia_initialized(vcpu->kvm)) return; /* Cleanup IMSIC context */ kvm_riscv_vcpu_aia_imsic_cleanup(vcpu); } int kvm_riscv_aia_inject_msi_by_id(struct kvm *kvm, u32 hart_index, u32 guest_index, u32 iid) { unsigned long idx; struct kvm_vcpu *vcpu; /* Proceed only if AIA was initialized successfully */ if (!kvm_riscv_aia_initialized(kvm)) return -EBUSY; /* Inject MSI to matching VCPU */ kvm_for_each_vcpu(idx, vcpu, kvm) { if (vcpu->arch.aia_context.hart_index == hart_index) return kvm_riscv_vcpu_aia_imsic_inject(vcpu, guest_index, 0, iid); } return 0; } int kvm_riscv_aia_inject_msi(struct kvm *kvm, struct kvm_msi *msi) { gpa_t tppn, ippn; unsigned long idx; struct kvm_vcpu *vcpu; u32 g, toff, iid = msi->data; struct kvm_aia *aia = &kvm->arch.aia; gpa_t target = (((gpa_t)msi->address_hi) << 32) | msi->address_lo; /* Proceed only if AIA was initialized successfully */ if (!kvm_riscv_aia_initialized(kvm)) return -EBUSY; /* Convert target address to target PPN */ tppn = target >> IMSIC_MMIO_PAGE_SHIFT; /* Extract and clear Guest ID from target PPN */ g = tppn & (BIT(aia->nr_guest_bits) - 1); tppn &= ~((gpa_t)(BIT(aia->nr_guest_bits) - 1)); /* Inject MSI to matching VCPU */ kvm_for_each_vcpu(idx, vcpu, kvm) { ippn = vcpu->arch.aia_context.imsic_addr >> IMSIC_MMIO_PAGE_SHIFT; if (ippn == tppn) { toff = target & (IMSIC_MMIO_PAGE_SZ - 1); return kvm_riscv_vcpu_aia_imsic_inject(vcpu, g, toff, iid); } } return 0; } int kvm_riscv_aia_inject_irq(struct kvm *kvm, unsigned int irq, bool level) { /* Proceed only if AIA was initialized successfully */ if (!kvm_riscv_aia_initialized(kvm)) return -EBUSY; /* Inject interrupt level change in APLIC */ return kvm_riscv_aia_aplic_inject(kvm, irq, level); } void kvm_riscv_aia_init_vm(struct kvm *kvm) { struct kvm_aia *aia = &kvm->arch.aia; if (!kvm_riscv_aia_available()) return; /* * We don't do any memory allocations over here because these * will be done after AIA device is initialized by the user-space. * * Refer, aia_init() implementation for more details. */ /* Initialize default values in AIA global context */ aia->mode = (kvm_riscv_aia_nr_hgei) ? KVM_DEV_RISCV_AIA_MODE_AUTO : KVM_DEV_RISCV_AIA_MODE_EMUL; aia->nr_ids = kvm_riscv_aia_max_ids - 1; aia->nr_sources = 0; aia->nr_group_bits = 0; aia->nr_group_shift = KVM_DEV_RISCV_AIA_GROUP_SHIFT_MIN; aia->nr_hart_bits = 0; aia->nr_guest_bits = 0; aia->aplic_addr = KVM_RISCV_AIA_UNDEF_ADDR; } void kvm_riscv_aia_destroy_vm(struct kvm *kvm) { /* Proceed only if AIA was initialized successfully */ if (!kvm_riscv_aia_initialized(kvm)) return; /* Cleanup APLIC context */ kvm_riscv_aia_aplic_cleanup(kvm); }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1