arch/arm64/kvm/vgic/vgic-v3.c

Source file repositories/reference/linux-study-clean/arch/arm64/kvm/vgic/vgic-v3.c

File Facts

System
Linux kernel
Corpus path
arch/arm64/kvm/vgic/vgic-v3.c
Extension
.c
Size
26768 bytes
Lines
1011
Domain
Architecture Layer
Bucket
arch/arm64
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

if (is_v2_sgi) {
			u8 cpuid = FIELD_GET(GICH_LR_PHYSID_CPUID, val);

			if (irq->active)
				irq->active_source = cpuid;

			if (val & ICH_LR_PENDING_BIT)
				irq->source |= BIT(cpuid);
		}

		/* Handle resampling for mapped interrupts if required */
		vgic_irq_handle_resampling(irq, deactivated, val & ICH_LR_PENDING_BIT);

		irq->on_lr = false;
	}

	/* Notify fds when the guest EOI'ed a level-triggered SPI, and drop the refcount */
	if (deactivated && lr_signals_eoi_mi(val) && vgic_valid_spi(vcpu->kvm, intid)) {
		kvm_notify_acked_irq(vcpu->kvm, 0,
				     intid - VGIC_NR_PRIVATE_IRQS);
		atomic_dec_if_positive(&vcpu->kvm->arch.vgic.active_spis);
	}

	vgic_put_irq(vcpu->kvm, irq);
}

static u64 vgic_v3_compute_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq);

static void vgic_v3_deactivate_phys(u32 intid)
{
	if (cpus_have_final_cap(ARM64_HAS_GICV5_LEGACY))
		gic_insn(intid | FIELD_PREP(GICV5_GIC_CDDI_TYPE_MASK, 1), CDDI);
	else
		gic_write_dir(intid);
}

void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
{
	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
	u32 eoicount = FIELD_GET(ICH_HCR_EL2_EOIcount, cpuif->vgic_hcr);
	struct vgic_irq *irq = *host_data_ptr(last_lr_irq);

	DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());

	for (int lr = 0; lr < cpuif->used_lrs; lr++)
		vgic_v3_fold_lr(vcpu, cpuif->vgic_lr[lr]);

	/*
	 * EOIMode=0: use EOIcount to emulate deactivation. We are
	 * guaranteed to deactivate in reverse order of the activation, so
	 * just pick one active interrupt after the other in the tail part
	 * of the ap_list, past the LRs, and replay the deactivation as if
	 * the CPU was doing it. We also rely on priority drop to have taken
	 * place, and the list to be sorted by priority.
	 */
	list_for_each_entry_continue(irq, &vgic_cpu->ap_list_head, ap_list) {
		u64 lr;

		/*
		 * I would have loved to write this using a scoped_guard(),
		 * but using 'continue' here is a total train wreck.
		 */
		if (!eoicount) {
			break;
		} else {
			guard(raw_spinlock)(&irq->irq_lock);

			if (!(likely(vgic_target_oracle(irq) == vcpu) &&
			      irq->active))
				continue;

			lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT;
		}

		if (lr & ICH_LR_HW)
			vgic_v3_deactivate_phys(FIELD_GET(ICH_LR_PHYS_ID_MASK, lr));

		vgic_v3_fold_lr(vcpu, lr);
		eoicount--;
	}

	cpuif->used_lrs = 0;
}

void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val)
{
	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
	struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3;
	u32 model = vcpu->kvm->arch.vgic.vgic_model;

Annotation

Implementation Notes