18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Linaro
48c2ecf20Sopenharmony_ci * Author: Christoffer Dall <christoffer.dall@linaro.org>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/cpu.h>
88c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/kvm_host.h>
118c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
128c2ecf20Sopenharmony_ci#include <kvm/arm_vgic.h>
138c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h>
148c2ecf20Sopenharmony_ci#include "vgic.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * Structure to control looping through the entire vgic state.  We start at
188c2ecf20Sopenharmony_ci * zero for each field and move upwards.  So, if dist_id is 0 we print the
198c2ecf20Sopenharmony_ci * distributor info.  When dist_id is 1, we have already printed it and move
208c2ecf20Sopenharmony_ci * on.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * When vcpu_id < nr_cpus we print the vcpu info until vcpu_id == nr_cpus and
238c2ecf20Sopenharmony_ci * so on.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_cistruct vgic_state_iter {
268c2ecf20Sopenharmony_ci	int nr_cpus;
278c2ecf20Sopenharmony_ci	int nr_spis;
288c2ecf20Sopenharmony_ci	int nr_lpis;
298c2ecf20Sopenharmony_ci	int dist_id;
308c2ecf20Sopenharmony_ci	int vcpu_id;
318c2ecf20Sopenharmony_ci	int intid;
328c2ecf20Sopenharmony_ci	int lpi_idx;
338c2ecf20Sopenharmony_ci	u32 *lpi_array;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void iter_next(struct vgic_state_iter *iter)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	if (iter->dist_id == 0) {
398c2ecf20Sopenharmony_ci		iter->dist_id++;
408c2ecf20Sopenharmony_ci		return;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	iter->intid++;
448c2ecf20Sopenharmony_ci	if (iter->intid == VGIC_NR_PRIVATE_IRQS &&
458c2ecf20Sopenharmony_ci	    ++iter->vcpu_id < iter->nr_cpus)
468c2ecf20Sopenharmony_ci		iter->intid = 0;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS)) {
498c2ecf20Sopenharmony_ci		if (iter->lpi_idx < iter->nr_lpis)
508c2ecf20Sopenharmony_ci			iter->intid = iter->lpi_array[iter->lpi_idx];
518c2ecf20Sopenharmony_ci		iter->lpi_idx++;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void iter_init(struct kvm *kvm, struct vgic_state_iter *iter,
568c2ecf20Sopenharmony_ci		      loff_t pos)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	int nr_cpus = atomic_read(&kvm->online_vcpus);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	memset(iter, 0, sizeof(*iter));
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	iter->nr_cpus = nr_cpus;
638c2ecf20Sopenharmony_ci	iter->nr_spis = kvm->arch.vgic.nr_spis;
648c2ecf20Sopenharmony_ci	if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
658c2ecf20Sopenharmony_ci		iter->nr_lpis = vgic_copy_lpi_list(kvm, NULL, &iter->lpi_array);
668c2ecf20Sopenharmony_ci		if (iter->nr_lpis < 0)
678c2ecf20Sopenharmony_ci			iter->nr_lpis = 0;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Fast forward to the right position if needed */
718c2ecf20Sopenharmony_ci	while (pos--)
728c2ecf20Sopenharmony_ci		iter_next(iter);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic bool end_of_vgic(struct vgic_state_iter *iter)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	return iter->dist_id > 0 &&
788c2ecf20Sopenharmony_ci		iter->vcpu_id == iter->nr_cpus &&
798c2ecf20Sopenharmony_ci		iter->intid >= (iter->nr_spis + VGIC_NR_PRIVATE_IRQS) &&
808c2ecf20Sopenharmony_ci		iter->lpi_idx > iter->nr_lpis;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void *vgic_debug_start(struct seq_file *s, loff_t *pos)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct kvm *kvm = (struct kvm *)s->private;
868c2ecf20Sopenharmony_ci	struct vgic_state_iter *iter;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	mutex_lock(&kvm->lock);
898c2ecf20Sopenharmony_ci	iter = kvm->arch.vgic.iter;
908c2ecf20Sopenharmony_ci	if (iter) {
918c2ecf20Sopenharmony_ci		iter = ERR_PTR(-EBUSY);
928c2ecf20Sopenharmony_ci		goto out;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
968c2ecf20Sopenharmony_ci	if (!iter) {
978c2ecf20Sopenharmony_ci		iter = ERR_PTR(-ENOMEM);
988c2ecf20Sopenharmony_ci		goto out;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	iter_init(kvm, iter, *pos);
1028c2ecf20Sopenharmony_ci	kvm->arch.vgic.iter = iter;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (end_of_vgic(iter))
1058c2ecf20Sopenharmony_ci		iter = NULL;
1068c2ecf20Sopenharmony_ciout:
1078c2ecf20Sopenharmony_ci	mutex_unlock(&kvm->lock);
1088c2ecf20Sopenharmony_ci	return iter;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic void *vgic_debug_next(struct seq_file *s, void *v, loff_t *pos)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct kvm *kvm = (struct kvm *)s->private;
1148c2ecf20Sopenharmony_ci	struct vgic_state_iter *iter = kvm->arch.vgic.iter;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	++*pos;
1178c2ecf20Sopenharmony_ci	iter_next(iter);
1188c2ecf20Sopenharmony_ci	if (end_of_vgic(iter))
1198c2ecf20Sopenharmony_ci		iter = NULL;
1208c2ecf20Sopenharmony_ci	return iter;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic void vgic_debug_stop(struct seq_file *s, void *v)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct kvm *kvm = (struct kvm *)s->private;
1268c2ecf20Sopenharmony_ci	struct vgic_state_iter *iter;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * If the seq file wasn't properly opened, there's nothing to clearn
1308c2ecf20Sopenharmony_ci	 * up.
1318c2ecf20Sopenharmony_ci	 */
1328c2ecf20Sopenharmony_ci	if (IS_ERR(v))
1338c2ecf20Sopenharmony_ci		return;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	mutex_lock(&kvm->lock);
1368c2ecf20Sopenharmony_ci	iter = kvm->arch.vgic.iter;
1378c2ecf20Sopenharmony_ci	kfree(iter->lpi_array);
1388c2ecf20Sopenharmony_ci	kfree(iter);
1398c2ecf20Sopenharmony_ci	kvm->arch.vgic.iter = NULL;
1408c2ecf20Sopenharmony_ci	mutex_unlock(&kvm->lock);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void print_dist_state(struct seq_file *s, struct vgic_dist *dist)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	bool v3 = dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	seq_printf(s, "Distributor\n");
1488c2ecf20Sopenharmony_ci	seq_printf(s, "===========\n");
1498c2ecf20Sopenharmony_ci	seq_printf(s, "vgic_model:\t%s\n", v3 ? "GICv3" : "GICv2");
1508c2ecf20Sopenharmony_ci	seq_printf(s, "nr_spis:\t%d\n", dist->nr_spis);
1518c2ecf20Sopenharmony_ci	if (v3)
1528c2ecf20Sopenharmony_ci		seq_printf(s, "nr_lpis:\t%d\n", dist->lpi_list_count);
1538c2ecf20Sopenharmony_ci	seq_printf(s, "enabled:\t%d\n", dist->enabled);
1548c2ecf20Sopenharmony_ci	seq_printf(s, "\n");
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	seq_printf(s, "P=pending_latch, L=line_level, A=active\n");
1578c2ecf20Sopenharmony_ci	seq_printf(s, "E=enabled, H=hw, C=config (level=1, edge=0)\n");
1588c2ecf20Sopenharmony_ci	seq_printf(s, "G=group\n");
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void print_header(struct seq_file *s, struct vgic_irq *irq,
1628c2ecf20Sopenharmony_ci			 struct kvm_vcpu *vcpu)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int id = 0;
1658c2ecf20Sopenharmony_ci	char *hdr = "SPI ";
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (vcpu) {
1688c2ecf20Sopenharmony_ci		hdr = "VCPU";
1698c2ecf20Sopenharmony_ci		id = vcpu->vcpu_id;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	seq_printf(s, "\n");
1738c2ecf20Sopenharmony_ci	seq_printf(s, "%s%2d TYP   ID TGT_ID PLAEHCG     HWID   TARGET SRC PRI VCPU_ID\n", hdr, id);
1748c2ecf20Sopenharmony_ci	seq_printf(s, "----------------------------------------------------------------\n");
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void print_irq_state(struct seq_file *s, struct vgic_irq *irq,
1788c2ecf20Sopenharmony_ci			    struct kvm_vcpu *vcpu)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	char *type;
1818c2ecf20Sopenharmony_ci	bool pending;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (irq->intid < VGIC_NR_SGIS)
1848c2ecf20Sopenharmony_ci		type = "SGI";
1858c2ecf20Sopenharmony_ci	else if (irq->intid < VGIC_NR_PRIVATE_IRQS)
1868c2ecf20Sopenharmony_ci		type = "PPI";
1878c2ecf20Sopenharmony_ci	else if (irq->intid < VGIC_MAX_SPI)
1888c2ecf20Sopenharmony_ci		type = "SPI";
1898c2ecf20Sopenharmony_ci	else
1908c2ecf20Sopenharmony_ci		type = "LPI";
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (irq->intid ==0 || irq->intid == VGIC_NR_PRIVATE_IRQS)
1938c2ecf20Sopenharmony_ci		print_header(s, irq, vcpu);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	pending = irq->pending_latch;
1968c2ecf20Sopenharmony_ci	if (irq->hw && vgic_irq_is_sgi(irq->intid)) {
1978c2ecf20Sopenharmony_ci		int err;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		err = irq_get_irqchip_state(irq->host_irq,
2008c2ecf20Sopenharmony_ci					    IRQCHIP_STATE_PENDING,
2018c2ecf20Sopenharmony_ci					    &pending);
2028c2ecf20Sopenharmony_ci		WARN_ON_ONCE(err);
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	seq_printf(s, "       %s %4d "
2068c2ecf20Sopenharmony_ci		      "    %2d "
2078c2ecf20Sopenharmony_ci		      "%d%d%d%d%d%d%d "
2088c2ecf20Sopenharmony_ci		      "%8d "
2098c2ecf20Sopenharmony_ci		      "%8x "
2108c2ecf20Sopenharmony_ci		      " %2x "
2118c2ecf20Sopenharmony_ci		      "%3d "
2128c2ecf20Sopenharmony_ci		      "     %2d "
2138c2ecf20Sopenharmony_ci		      "\n",
2148c2ecf20Sopenharmony_ci			type, irq->intid,
2158c2ecf20Sopenharmony_ci			(irq->target_vcpu) ? irq->target_vcpu->vcpu_id : -1,
2168c2ecf20Sopenharmony_ci			pending,
2178c2ecf20Sopenharmony_ci			irq->line_level,
2188c2ecf20Sopenharmony_ci			irq->active,
2198c2ecf20Sopenharmony_ci			irq->enabled,
2208c2ecf20Sopenharmony_ci			irq->hw,
2218c2ecf20Sopenharmony_ci			irq->config == VGIC_CONFIG_LEVEL,
2228c2ecf20Sopenharmony_ci			irq->group,
2238c2ecf20Sopenharmony_ci			irq->hwintid,
2248c2ecf20Sopenharmony_ci			irq->mpidr,
2258c2ecf20Sopenharmony_ci			irq->source,
2268c2ecf20Sopenharmony_ci			irq->priority,
2278c2ecf20Sopenharmony_ci			(irq->vcpu) ? irq->vcpu->vcpu_id : -1);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int vgic_debug_show(struct seq_file *s, void *v)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct kvm *kvm = (struct kvm *)s->private;
2338c2ecf20Sopenharmony_ci	struct vgic_state_iter *iter = (struct vgic_state_iter *)v;
2348c2ecf20Sopenharmony_ci	struct vgic_irq *irq;
2358c2ecf20Sopenharmony_ci	struct kvm_vcpu *vcpu = NULL;
2368c2ecf20Sopenharmony_ci	unsigned long flags;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (iter->dist_id == 0) {
2398c2ecf20Sopenharmony_ci		print_dist_state(s, &kvm->arch.vgic);
2408c2ecf20Sopenharmony_ci		return 0;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (!kvm->arch.vgic.initialized)
2448c2ecf20Sopenharmony_ci		return 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (iter->vcpu_id < iter->nr_cpus)
2478c2ecf20Sopenharmony_ci		vcpu = kvm_get_vcpu(kvm, iter->vcpu_id);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	irq = vgic_get_irq(kvm, vcpu, iter->intid);
2508c2ecf20Sopenharmony_ci	if (!irq) {
2518c2ecf20Sopenharmony_ci		seq_printf(s, "       LPI %4d freed\n", iter->intid);
2528c2ecf20Sopenharmony_ci		return 0;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&irq->irq_lock, flags);
2568c2ecf20Sopenharmony_ci	print_irq_state(s, irq, vcpu);
2578c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	vgic_put_irq(kvm, irq);
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic const struct seq_operations vgic_debug_sops = {
2648c2ecf20Sopenharmony_ci	.start = vgic_debug_start,
2658c2ecf20Sopenharmony_ci	.next  = vgic_debug_next,
2668c2ecf20Sopenharmony_ci	.stop  = vgic_debug_stop,
2678c2ecf20Sopenharmony_ci	.show  = vgic_debug_show
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ciDEFINE_SEQ_ATTRIBUTE(vgic_debug);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_civoid vgic_debug_init(struct kvm *kvm)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	debugfs_create_file("vgic-state", 0444, kvm->debugfs_dentry, kvm,
2758c2ecf20Sopenharmony_ci			    &vgic_debug_fops);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_civoid vgic_debug_destroy(struct kvm *kvm)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci}
281