[PATCH 41/55] KVM: arm64: vgic-its: Implement MSI injection in ITS emulation

Christoffer Dall christoffer.dall at linaro.org
Mon Aug 1 11:20:06 PDT 2016


On Fri, Jul 22, 2016 at 06:28:58PM +0100, Marc Zyngier wrote:
> From: Andre Przywara <andre.przywara at arm.com>
> 
> When userland wants to inject an MSI into the guest, it uses the
> KVM_SIGNAL_MSI ioctl, which carries the doorbell address along with
> the payload and the device ID.
> With the help of the KVM IO bus framework we learn the corresponding
> ITS from the doorbell address. We then use our wrapper functions to
> iterate the linked lists and find the proper Interrupt Translation Table
> Entry (ITTE) and thus the corresponding struct vgic_irq to finally set
> the pending bit.
> We also provide the handler for the ITS "INT" command, which allows a
> guest to trigger an MSI via the ITS command queue. Since this one knows
> about the right ITS already, we directly call the MMIO handler function
> without using the kvm_io_bus framework.
> 
> Signed-off-by: Andre Przywara <andre.przywara at arm.com>
> Reviewed-by: Marc Zyngier <marc.zyngier at arm.com>
> Tested-by: Eric Auger <eric.auger at redhat.com>
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> ---
>  virt/kvm/arm/vgic/vgic-its.c | 77 ++++++++++++++++++++++++++++++++++++++++++++
>  virt/kvm/arm/vgic/vgic.h     |  6 ++++
>  2 files changed, 83 insertions(+)
> 
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index 1408c88..d8e8f14 100644
> --- a/virt/kvm/arm/vgic/vgic-its.c
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -437,6 +437,65 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
>  	return 0;
>  }
>  
> +/*
> + * Find the target VCPU and the LPI number for a given devid/eventid pair
> + * and make this IRQ pending, possibly injecting it.
> + * Must be called with the its_lock mutex held.
> + */
> +static void vgic_its_trigger_msi(struct kvm *kvm, struct vgic_its *its,
> +				 u32 devid, u32 eventid)
> +{
> +	struct its_itte *itte;
> +
> +	if (!its->enabled)
> +		return;
> +
> +	itte = find_itte(its, devid, eventid);
> +	/* Triggering an unmapped IRQ gets silently dropped. */
> +	if (itte && its_is_collection_mapped(itte->collection)) {
> +		struct kvm_vcpu *vcpu;
> +
> +		vcpu = kvm_get_vcpu(kvm, itte->collection->target_addr);
> +		if (vcpu && vcpu->arch.vgic_cpu.lpis_enabled) {
> +			spin_lock(&itte->irq->irq_lock);
> +			itte->irq->pending = true;
> +			vgic_queue_irq_unlock(kvm, itte->irq);
> +		}
> +	}
> +}
> +
> +/*
> + * Queries the KVM IO bus framework to get the ITS pointer from the given
> + * doorbell address.
> + * We then call vgic_its_trigger_msi() with the decoded data.
> + */
> +int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
> +{
> +	u64 address;
> +	struct kvm_io_device *kvm_io_dev;
> +	struct vgic_io_device *iodev;
> +
> +	if (!vgic_has_its(kvm))
> +		return -ENODEV;
> +
> +	if (!(msi->flags & KVM_MSI_VALID_DEVID))
> +		return -EINVAL;
> +
> +	address = (u64)msi->address_hi << 32 | msi->address_lo;
> +
> +	kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, address);
> +	if (!kvm_io_dev)
> +		return -ENODEV;
> +
> +	iodev = container_of(kvm_io_dev, struct vgic_io_device, dev);
> +
> +	mutex_lock(&iodev->its->its_lock);
> +	vgic_its_trigger_msi(kvm, iodev->its, msi->devid, msi->data);
> +	mutex_unlock(&iodev->its->its_lock);
> +
> +	return 0;

According to KVM_SIGNAL_MSI this means that the guest blocked the MSI.
Is this the intention, or is the return value translated somewhere
higher in the call stack?

> +}
> +
>  /* Requires the its_lock to be held. */
>  static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
>  {
> @@ -897,6 +956,21 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
>  }
>  
>  /*
> + * The INT command injects the LPI associated with that DevID/EvID pair.
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_int(struct kvm *kvm, struct vgic_its *its,
> +				   u64 *its_cmd)
> +{
> +	u32 msi_data = its_cmd_get_id(its_cmd);
> +	u64 msi_devid = its_cmd_get_deviceid(its_cmd);
> +
> +	vgic_its_trigger_msi(kvm, its, msi_devid, msi_data);
> +
> +	return 0;
> +}
> +
> +/*
>   * This function is called with the its_cmd lock held, but the ITS data
>   * structure lock dropped.
>   */
> @@ -932,6 +1006,9 @@ static int vgic_its_handle_command(struct kvm *kvm, struct vgic_its *its,
>  	case GITS_CMD_MOVALL:
>  		ret = vgic_its_cmd_handle_movall(kvm, its, its_cmd);
>  		break;
> +	case GITS_CMD_INT:
> +		ret = vgic_its_cmd_handle_int(kvm, its, its_cmd);
> +		break;
>  	case GITS_CMD_INV:
>  		ret = vgic_its_cmd_handle_inv(kvm, its, its_cmd);
>  		break;
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index ee348de..9d557f2 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -78,6 +78,7 @@ int vgic_v3_map_resources(struct kvm *kvm);
>  int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
>  bool vgic_has_its(struct kvm *kvm);
>  void vgic_enable_lpis(struct kvm_vcpu *vcpu);
> +int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
>  #else
>  static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
>  {
> @@ -138,6 +139,11 @@ static inline bool vgic_has_its(struct kvm *kvm)
>  static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
>  {
>  }
> +
> +static inline int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
> +{
> +	return -ENODEV;
> +}
>  #endif
>  
>  int kvm_register_vgic_device(unsigned long type);
> -- 
> 2.8.1
> 



More information about the linux-arm-kernel mailing list