Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/arch/riscv/kvm/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 15 kB image not shown  

Quelle  aia_device.c   Sprache: C

 
// 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 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 (kvm_trylock_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:
 kvm_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;

 if (!kvm_riscv_aia_available())
  return;
 memset(csr, 0, 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);
}

void kvm_riscv_vcpu_aia_init(struct kvm_vcpu *vcpu)
{
 struct kvm_vcpu_aia *vaia = &vcpu->arch.aia_context;

 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 vcpu context */
 vaia->imsic_addr = KVM_RISCV_AIA_UNDEF_ADDR;
 vaia->hart_index = vcpu->vcpu_idx;
}

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);
}

Messung V0.5
C=94 H=78 G=86

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.