162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 Linaro
462306a36Sopenharmony_ci * Author: Christoffer Dall <christoffer.dall@linaro.org>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/cpu.h>
862306a36Sopenharmony_ci#include <linux/debugfs.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/kvm_host.h>
1162306a36Sopenharmony_ci#include <linux/seq_file.h>
1262306a36Sopenharmony_ci#include <kvm/arm_vgic.h>
1362306a36Sopenharmony_ci#include <asm/kvm_mmu.h>
1462306a36Sopenharmony_ci#include "vgic.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * Structure to control looping through the entire vgic state.  We start at
1862306a36Sopenharmony_ci * zero for each field and move upwards.  So, if dist_id is 0 we print the
1962306a36Sopenharmony_ci * distributor info.  When dist_id is 1, we have already printed it and move
2062306a36Sopenharmony_ci * on.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * When vcpu_id < nr_cpus we print the vcpu info until vcpu_id == nr_cpus and
2362306a36Sopenharmony_ci * so on.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistruct vgic_state_iter {
2662306a36Sopenharmony_ci	int nr_cpus;
2762306a36Sopenharmony_ci	int nr_spis;
2862306a36Sopenharmony_ci	int nr_lpis;
2962306a36Sopenharmony_ci	int dist_id;
3062306a36Sopenharmony_ci	int vcpu_id;
3162306a36Sopenharmony_ci	int intid;
3262306a36Sopenharmony_ci	int lpi_idx;
3362306a36Sopenharmony_ci	u32 *lpi_array;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void iter_next(struct vgic_state_iter *iter)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	if (iter->dist_id == 0) {
3962306a36Sopenharmony_ci		iter->dist_id++;
4062306a36Sopenharmony_ci		return;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	iter->intid++;
4462306a36Sopenharmony_ci	if (iter->intid == VGIC_NR_PRIVATE_IRQS &&
4562306a36Sopenharmony_ci	    ++iter->vcpu_id < iter->nr_cpus)
4662306a36Sopenharmony_ci		iter->intid = 0;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS)) {
4962306a36Sopenharmony_ci		if (iter->lpi_idx < iter->nr_lpis)
5062306a36Sopenharmony_ci			iter->intid = iter->lpi_array[iter->lpi_idx];
5162306a36Sopenharmony_ci		iter->lpi_idx++;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void iter_init(struct kvm *kvm, struct vgic_state_iter *iter,
5662306a36Sopenharmony_ci		      loff_t pos)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	int nr_cpus = atomic_read(&kvm->online_vcpus);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	memset(iter, 0, sizeof(*iter));
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	iter->nr_cpus = nr_cpus;
6362306a36Sopenharmony_ci	iter->nr_spis = kvm->arch.vgic.nr_spis;
6462306a36Sopenharmony_ci	if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
6562306a36Sopenharmony_ci		iter->nr_lpis = vgic_copy_lpi_list(kvm, NULL, &iter->lpi_array);
6662306a36Sopenharmony_ci		if (iter->nr_lpis < 0)
6762306a36Sopenharmony_ci			iter->nr_lpis = 0;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Fast forward to the right position if needed */
7162306a36Sopenharmony_ci	while (pos--)
7262306a36Sopenharmony_ci		iter_next(iter);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic bool end_of_vgic(struct vgic_state_iter *iter)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	return iter->dist_id > 0 &&
7862306a36Sopenharmony_ci		iter->vcpu_id == iter->nr_cpus &&
7962306a36Sopenharmony_ci		iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS) &&
8062306a36Sopenharmony_ci		iter->lpi_idx > iter->nr_lpis;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void *vgic_debug_start(struct seq_file *s, loff_t *pos)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct kvm *kvm = s->private;
8662306a36Sopenharmony_ci	struct vgic_state_iter *iter;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	mutex_lock(&kvm->arch.config_lock);
8962306a36Sopenharmony_ci	iter = kvm->arch.vgic.iter;
9062306a36Sopenharmony_ci	if (iter) {
9162306a36Sopenharmony_ci		iter = ERR_PTR(-EBUSY);
9262306a36Sopenharmony_ci		goto out;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
9662306a36Sopenharmony_ci	if (!iter) {
9762306a36Sopenharmony_ci		iter = ERR_PTR(-ENOMEM);
9862306a36Sopenharmony_ci		goto out;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	iter_init(kvm, iter, *pos);
10262306a36Sopenharmony_ci	kvm->arch.vgic.iter = iter;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (end_of_vgic(iter))
10562306a36Sopenharmony_ci		iter = NULL;
10662306a36Sopenharmony_ciout:
10762306a36Sopenharmony_ci	mutex_unlock(&kvm->arch.config_lock);
10862306a36Sopenharmony_ci	return iter;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void *vgic_debug_next(struct seq_file *s, void *v, loff_t *pos)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct kvm *kvm = s->private;
11462306a36Sopenharmony_ci	struct vgic_state_iter *iter = kvm->arch.vgic.iter;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	++*pos;
11762306a36Sopenharmony_ci	iter_next(iter);
11862306a36Sopenharmony_ci	if (end_of_vgic(iter))
11962306a36Sopenharmony_ci		iter = NULL;
12062306a36Sopenharmony_ci	return iter;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void vgic_debug_stop(struct seq_file *s, void *v)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct kvm *kvm = s->private;
12662306a36Sopenharmony_ci	struct vgic_state_iter *iter;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * If the seq file wasn't properly opened, there's nothing to clearn
13062306a36Sopenharmony_ci	 * up.
13162306a36Sopenharmony_ci	 */
13262306a36Sopenharmony_ci	if (IS_ERR(v))
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	mutex_lock(&kvm->arch.config_lock);
13662306a36Sopenharmony_ci	iter = kvm->arch.vgic.iter;
13762306a36Sopenharmony_ci	kfree(iter->lpi_array);
13862306a36Sopenharmony_ci	kfree(iter);
13962306a36Sopenharmony_ci	kvm->arch.vgic.iter = NULL;
14062306a36Sopenharmony_ci	mutex_unlock(&kvm->arch.config_lock);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void print_dist_state(struct seq_file *s, struct vgic_dist *dist)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	bool v3 = dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	seq_printf(s, "Distributor\n");
14862306a36Sopenharmony_ci	seq_printf(s, "===========\n");
14962306a36Sopenharmony_ci	seq_printf(s, "vgic_model:\t%s\n", v3 ? "GICv3" : "GICv2");
15062306a36Sopenharmony_ci	seq_printf(s, "nr_spis:\t%d\n", dist->nr_spis);
15162306a36Sopenharmony_ci	if (v3)
15262306a36Sopenharmony_ci		seq_printf(s, "nr_lpis:\t%d\n", dist->lpi_list_count);
15362306a36Sopenharmony_ci	seq_printf(s, "enabled:\t%d\n", dist->enabled);
15462306a36Sopenharmony_ci	seq_printf(s, "\n");
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	seq_printf(s, "P=pending_latch, L=line_level, A=active\n");
15762306a36Sopenharmony_ci	seq_printf(s, "E=enabled, H=hw, C=config (level=1, edge=0)\n");
15862306a36Sopenharmony_ci	seq_printf(s, "G=group\n");
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void print_header(struct seq_file *s, struct vgic_irq *irq,
16262306a36Sopenharmony_ci			 struct kvm_vcpu *vcpu)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	int id = 0;
16562306a36Sopenharmony_ci	char *hdr = "SPI ";
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (vcpu) {
16862306a36Sopenharmony_ci		hdr = "VCPU";
16962306a36Sopenharmony_ci		id = vcpu->vcpu_id;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	seq_printf(s, "\n");
17362306a36Sopenharmony_ci	seq_printf(s, "%s%2d TYP   ID TGT_ID PLAEHCG     HWID   TARGET SRC PRI VCPU_ID\n", hdr, id);
17462306a36Sopenharmony_ci	seq_printf(s, "----------------------------------------------------------------\n");
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
17862306a36Sopenharmony_ci			    struct kvm_vcpu *vcpu)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	char *type;
18162306a36Sopenharmony_ci	bool pending;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (irq->intid < VGIC_NR_SGIS)
18462306a36Sopenharmony_ci		type = "SGI";
18562306a36Sopenharmony_ci	else if (irq->intid < VGIC_NR_PRIVATE_IRQS)
18662306a36Sopenharmony_ci		type = "PPI";
18762306a36Sopenharmony_ci	else if (irq->intid < VGIC_MAX_SPI)
18862306a36Sopenharmony_ci		type = "SPI";
18962306a36Sopenharmony_ci	else
19062306a36Sopenharmony_ci		type = "LPI";
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (irq->intid ==0 || irq->intid == VGIC_NR_PRIVATE_IRQS)
19362306a36Sopenharmony_ci		print_header(s, irq, vcpu);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	pending = irq->pending_latch;
19662306a36Sopenharmony_ci	if (irq->hw && vgic_irq_is_sgi(irq->intid)) {
19762306a36Sopenharmony_ci		int err;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		err = irq_get_irqchip_state(irq->host_irq,
20062306a36Sopenharmony_ci					    IRQCHIP_STATE_PENDING,
20162306a36Sopenharmony_ci					    &pending);
20262306a36Sopenharmony_ci		WARN_ON_ONCE(err);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	seq_printf(s, "       %s %4d "
20662306a36Sopenharmony_ci		      "    %2d "
20762306a36Sopenharmony_ci		      "%d%d%d%d%d%d%d "
20862306a36Sopenharmony_ci		      "%8d "
20962306a36Sopenharmony_ci		      "%8x "
21062306a36Sopenharmony_ci		      " %2x "
21162306a36Sopenharmony_ci		      "%3d "
21262306a36Sopenharmony_ci		      "     %2d "
21362306a36Sopenharmony_ci		      "\n",
21462306a36Sopenharmony_ci			type, irq->intid,
21562306a36Sopenharmony_ci			(irq->target_vcpu) ? irq->target_vcpu->vcpu_id : -1,
21662306a36Sopenharmony_ci			pending,
21762306a36Sopenharmony_ci			irq->line_level,
21862306a36Sopenharmony_ci			irq->active,
21962306a36Sopenharmony_ci			irq->enabled,
22062306a36Sopenharmony_ci			irq->hw,
22162306a36Sopenharmony_ci			irq->config == VGIC_CONFIG_LEVEL,
22262306a36Sopenharmony_ci			irq->group,
22362306a36Sopenharmony_ci			irq->hwintid,
22462306a36Sopenharmony_ci			irq->mpidr,
22562306a36Sopenharmony_ci			irq->source,
22662306a36Sopenharmony_ci			irq->priority,
22762306a36Sopenharmony_ci			(irq->vcpu) ? irq->vcpu->vcpu_id : -1);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int vgic_debug_show(struct seq_file *s, void *v)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct kvm *kvm = s->private;
23362306a36Sopenharmony_ci	struct vgic_state_iter *iter = v;
23462306a36Sopenharmony_ci	struct vgic_irq *irq;
23562306a36Sopenharmony_ci	struct kvm_vcpu *vcpu = NULL;
23662306a36Sopenharmony_ci	unsigned long flags;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (iter->dist_id == 0) {
23962306a36Sopenharmony_ci		print_dist_state(s, &kvm->arch.vgic);
24062306a36Sopenharmony_ci		return 0;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (!kvm->arch.vgic.initialized)
24462306a36Sopenharmony_ci		return 0;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (iter->vcpu_id < iter->nr_cpus)
24762306a36Sopenharmony_ci		vcpu = kvm_get_vcpu(kvm, iter->vcpu_id);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	irq = vgic_get_irq(kvm, vcpu, iter->intid);
25062306a36Sopenharmony_ci	if (!irq) {
25162306a36Sopenharmony_ci		seq_printf(s, "       LPI %4d freed\n", iter->intid);
25262306a36Sopenharmony_ci		return 0;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	raw_spin_lock_irqsave(&irq->irq_lock, flags);
25662306a36Sopenharmony_ci	print_irq_state(s, irq, vcpu);
25762306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	vgic_put_irq(kvm, irq);
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic const struct seq_operations vgic_debug_sops = {
26462306a36Sopenharmony_ci	.start = vgic_debug_start,
26562306a36Sopenharmony_ci	.next  = vgic_debug_next,
26662306a36Sopenharmony_ci	.stop  = vgic_debug_stop,
26762306a36Sopenharmony_ci	.show  = vgic_debug_show
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ciDEFINE_SEQ_ATTRIBUTE(vgic_debug);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_civoid vgic_debug_init(struct kvm *kvm)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	debugfs_create_file("vgic-state", 0444, kvm->debugfs_dentry, kvm,
27562306a36Sopenharmony_ci			    &vgic_debug_fops);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_civoid vgic_debug_destroy(struct kvm *kvm)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci}
281