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