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