[PATCH RESEND v2 7/8] KVM: arm-vgic: Add GICD_SPENDSGIR and GICD_CPENDSGIR handlers

Marc Zyngier maz at misterjones.org
Wed Oct 23 12:00:43 EDT 2013


On 2013-10-22 10:08, Christoffer Dall wrote:
> Handle MMIO accesses to the two registers which should support both 
> the
> case where the VMs want to read/write either of these registers and 
> the
> case where user space reads/writes these registers to do save/restore 
> of
> the VGIC state.
>
> Note that the added complexity compared to simple set/clear enable
> registers stems from the bookkeping of source cpu ids.  It may be
> possible to change the underlying data structure to simplify the
> complexity, but since this is not in the critical path, at all, this 
> is
> left as an interesting excercise to the reader.
>
> Signed-off-by: Christoffer Dall <christoffer.dall at linaro.org>
> Reviewed-by: Alexander Graf <agraf at suse.de>
>
> ---
> Changelog[v2]:
>  - Use struct kvm_exit_mmio accessors for ->data field.
> ---
>  virt/kvm/arm/vgic.c |  114
> ++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 112 insertions(+), 2 deletions(-)
>
> diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
> index f2dc72a..4e8c3ab 100644
> --- a/virt/kvm/arm/vgic.c
> +++ b/virt/kvm/arm/vgic.c
> @@ -589,18 +589,128 @@ static bool handle_mmio_sgi_reg(struct 
> kvm_vcpu *vcpu,
>  	return false;
>  }
>
> +static void read_sgi_set_clear(struct kvm_vcpu *vcpu,
> +			       struct kvm_exit_mmio *mmio,
> +			       phys_addr_t offset)

set_clear is a bit unclear. How about reset?

> +{
> +	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
> +	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> +	int i, sgi, cpu;
> +	int min_sgi = (offset & ~0x3) * 4;
> +	int max_sgi = min_sgi + 3;
> +	int vcpu_id = vcpu->vcpu_id;
> +	u32 lr, reg = 0;
> +
> +	/* Copy source SGIs from distributor side */
> +	for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
> +		int shift = 8 * (sgi - min_sgi);
> +		reg |= (u32)dist->irq_sgi_sources[vcpu_id][sgi] << shift;
> +	}
> +
> +	/* Copy source SGIs already on LRs */
> +	for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
> +		lr = vgic_cpu->vgic_lr[i];
> +		sgi = lr & GICH_LR_VIRTUALID;
> +		cpu = (lr & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT;

Please wrap these  lr accesses into separate functions. There is quite 
a bit of duplication in this patch and I wonder if we could factor 
things a bit.

At least, please isolate what is emulation related from what is 
actually what the underlying HW provides. It will help mitigating my 
headache in the future... ;-)

> +		if (sgi >= min_sgi && sgi <= max_sgi) {
> +			if (lr & GICH_LR_STATE)
> +				reg |= (1 << cpu) << (8 * (sgi - min_sgi));
> +		}
> +	}
> +
> +	mmio_data_write(mmio, ~0, reg);
> +}
> +
>  static bool handle_mmio_sgi_clear(struct kvm_vcpu *vcpu,
>  				  struct kvm_exit_mmio *mmio,
>  				  phys_addr_t offset)
>  {
> -	return false;
> +	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
> +	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> +	int i, sgi, cpu;
> +	int min_sgi = (offset & ~0x3) * 4;
> +	int max_sgi = min_sgi + 3;
> +	int vcpu_id = vcpu->vcpu_id;
> +	u32 *lr, reg;
> +	bool updated = false;
> +
> +	if (!mmio->is_write) {
> +		read_sgi_set_clear(vcpu, mmio, offset);
> +		return false;
> +	}
> +
> +	reg = mmio_data_read(mmio, ~0);
> +
> +	/* Clear pending SGIs on distributor side */
> +	for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
> +		u8 mask = reg >> (8 * (sgi - min_sgi));
> +		if (dist->irq_sgi_sources[vcpu_id][sgi] & mask)
> +			updated = true;
> +		dist->irq_sgi_sources[vcpu_id][sgi] &= ~mask;
> +	}
> +
> +	/* Clear SGIs already on LRs */
> +	for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
> +		lr = &vgic_cpu->vgic_lr[i];
> +		sgi = *lr & GICH_LR_VIRTUALID;
> +		cpu = (*lr & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT;
> +
> +		if (sgi >= min_sgi && sgi <= max_sgi) {
> +			if (reg & ((1 << cpu) << (8 * (sgi - min_sgi)))) {
> +				if (*lr & GICH_LR_PENDING_BIT)
> +					updated = true;
> +				*lr &= GICH_LR_PENDING_BIT;
> +			}
> +		}
> +	}
> +
> +	return updated;
>  }
>
>  static bool handle_mmio_sgi_set(struct kvm_vcpu *vcpu,
>  				struct kvm_exit_mmio *mmio,
>  				phys_addr_t offset)
>  {
> -	return false;
> +	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
> +	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
> +	int i, sgi, cpu;
> +	int min_sgi = (offset & ~0x3) * 4;
> +	int max_sgi = min_sgi + 3;
> +	int vcpu_id = vcpu->vcpu_id;
> +	u32 *lr, reg;
> +	bool updated = false;
> +
> +	if (!mmio->is_write) {
> +		read_sgi_set_clear(vcpu, mmio, offset);
> +		return false;
> +	}
> +
> +	reg = mmio_data_read(mmio, ~0);
> +
> +	/* Set pending SGIs on distributor side */
> +	for (sgi = min_sgi; sgi <= max_sgi; sgi++) {
> +		u8 mask = reg >> (8 * (sgi - min_sgi));
> +		if ((dist->irq_sgi_sources[vcpu_id][sgi] & mask) != mask)
> +			updated = true;
> +		dist->irq_sgi_sources[vcpu_id][sgi] |= mask;
> +	}
> +
> +	/* Set active SGIs already on LRs to pending and active */
> +	for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
> +		lr = &vgic_cpu->vgic_lr[i];
> +		sgi = *lr & GICH_LR_VIRTUALID;
> +		cpu = (*lr & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT;
> +
> +		if (sgi >= min_sgi && sgi <= max_sgi) {
> +			if (reg & ((1 << cpu) << (8 * (sgi - min_sgi)))) {
> +				if (!(*lr & GICH_LR_PENDING_BIT))
> +					updated = true;
> +				*lr |= GICH_LR_PENDING_BIT;
> +			}
> +		}
> +	}
> +
> +	return updated;
>  }
>
>  /*

Overall, I feel like I've read the same function three times. Hint, 
hint... ;-)

         M.
-- 
Who you jivin' with that Cosmik Debris?



More information about the linux-arm-kernel mailing list