2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-17 23:09:13 +01:00
/*
* Copyright ( C ) 2016 Linaro
* Author : Christoffer Dall < christoffer . dall @ linaro . org >
*/
# include <linux/cpu.h>
# include <linux/debugfs.h>
# include <linux/interrupt.h>
# include <linux/kvm_host.h>
# include <linux/seq_file.h>
# include <kvm/arm_vgic.h>
# include <asm/kvm_mmu.h>
# include "vgic.h"
/*
* Structure to control looping through the entire vgic state . We start at
* zero for each field and move upwards . So , if dist_id is 0 we print the
* distributor info . When dist_id is 1 , we have already printed it and move
* on .
*
* When vcpu_id < nr_cpus we print the vcpu info until vcpu_id = = nr_cpus and
* so on .
*/
struct vgic_state_iter {
int nr_cpus ;
int nr_spis ;
2018-03-23 15:18:26 +00:00
int nr_lpis ;
2017-01-17 23:09:13 +01:00
int dist_id ;
int vcpu_id ;
int intid ;
2018-03-23 15:18:26 +00:00
int lpi_idx ;
u32 * lpi_array ;
2017-01-17 23:09:13 +01:00
} ;
static void iter_next ( struct vgic_state_iter * iter )
{
if ( iter - > dist_id = = 0 ) {
iter - > dist_id + + ;
return ;
}
iter - > intid + + ;
if ( iter - > intid = = VGIC_NR_PRIVATE_IRQS & &
+ + iter - > vcpu_id < iter - > nr_cpus )
iter - > intid = 0 ;
2018-03-23 15:18:26 +00:00
if ( iter - > intid > = ( iter - > nr_spis + VGIC_NR_PRIVATE_IRQS ) ) {
if ( iter - > lpi_idx < iter - > nr_lpis )
iter - > intid = iter - > lpi_array [ iter - > lpi_idx ] ;
iter - > lpi_idx + + ;
}
2017-01-17 23:09:13 +01:00
}
static void iter_init ( struct kvm * kvm , struct vgic_state_iter * iter ,
loff_t pos )
{
int nr_cpus = atomic_read ( & kvm - > online_vcpus ) ;
memset ( iter , 0 , sizeof ( * iter ) ) ;
iter - > nr_cpus = nr_cpus ;
iter - > nr_spis = kvm - > arch . vgic . nr_spis ;
2018-03-23 15:18:26 +00:00
if ( kvm - > arch . vgic . vgic_model = = KVM_DEV_TYPE_ARM_VGIC_V3 ) {
iter - > nr_lpis = vgic_copy_lpi_list ( kvm , NULL , & iter - > lpi_array ) ;
if ( iter - > nr_lpis < 0 )
iter - > nr_lpis = 0 ;
}
2017-01-17 23:09:13 +01:00
/* Fast forward to the right position if needed */
while ( pos - - )
iter_next ( iter ) ;
}
static bool end_of_vgic ( struct vgic_state_iter * iter )
{
return iter - > dist_id > 0 & &
iter - > vcpu_id = = iter - > nr_cpus & &
2018-03-23 15:18:26 +00:00
iter - > intid > = ( iter - > nr_spis + VGIC_NR_PRIVATE_IRQS ) & &
iter - > lpi_idx > iter - > nr_lpis ;
2017-01-17 23:09:13 +01:00
}
static void * vgic_debug_start ( struct seq_file * s , loff_t * pos )
{
struct kvm * kvm = ( struct kvm * ) s - > private ;
struct vgic_state_iter * iter ;
mutex_lock ( & kvm - > lock ) ;
iter = kvm - > arch . vgic . iter ;
if ( iter ) {
iter = ERR_PTR ( - EBUSY ) ;
goto out ;
}
iter = kmalloc ( sizeof ( * iter ) , GFP_KERNEL ) ;
if ( ! iter ) {
iter = ERR_PTR ( - ENOMEM ) ;
goto out ;
}
iter_init ( kvm , iter , * pos ) ;
kvm - > arch . vgic . iter = iter ;
if ( end_of_vgic ( iter ) )
iter = NULL ;
out :
mutex_unlock ( & kvm - > lock ) ;
return iter ;
}
static void * vgic_debug_next ( struct seq_file * s , void * v , loff_t * pos )
{
struct kvm * kvm = ( struct kvm * ) s - > private ;
struct vgic_state_iter * iter = kvm - > arch . vgic . iter ;
+ + * pos ;
iter_next ( iter ) ;
if ( end_of_vgic ( iter ) )
iter = NULL ;
return iter ;
}
static void vgic_debug_stop ( struct seq_file * s , void * v )
{
struct kvm * kvm = ( struct kvm * ) s - > private ;
struct vgic_state_iter * iter ;
/*
* If the seq file wasn ' t properly opened , there ' s nothing to clearn
* up .
*/
if ( IS_ERR ( v ) )
return ;
mutex_lock ( & kvm - > lock ) ;
iter = kvm - > arch . vgic . iter ;
2018-03-23 15:18:26 +00:00
kfree ( iter - > lpi_array ) ;
2017-01-17 23:09:13 +01:00
kfree ( iter ) ;
kvm - > arch . vgic . iter = NULL ;
mutex_unlock ( & kvm - > lock ) ;
}
static void print_dist_state ( struct seq_file * s , struct vgic_dist * dist )
{
2018-03-23 15:18:26 +00:00
bool v3 = dist - > vgic_model = = KVM_DEV_TYPE_ARM_VGIC_V3 ;
2017-01-17 23:09:13 +01:00
seq_printf ( s , " Distributor \n " ) ;
seq_printf ( s , " =========== \n " ) ;
2018-03-23 15:18:26 +00:00
seq_printf ( s , " vgic_model: \t %s \n " , v3 ? " GICv3 " : " GICv2 " ) ;
2017-01-17 23:09:13 +01:00
seq_printf ( s , " nr_spis: \t %d \n " , dist - > nr_spis ) ;
2018-03-23 15:18:26 +00:00
if ( v3 )
seq_printf ( s , " nr_lpis: \t %d \n " , dist - > lpi_list_count ) ;
2017-01-17 23:09:13 +01:00
seq_printf ( s , " enabled: \t %d \n " , dist - > enabled ) ;
seq_printf ( s , " \n " ) ;
seq_printf ( s , " P=pending_latch, L=line_level, A=active \n " ) ;
seq_printf ( s , " E=enabled, H=hw, C=config (level=1, edge=0) \n " ) ;
2018-07-16 15:06:21 +02:00
seq_printf ( s , " G=group \n " ) ;
2017-01-17 23:09:13 +01:00
}
static void print_header ( struct seq_file * s , struct vgic_irq * irq ,
struct kvm_vcpu * vcpu )
{
int id = 0 ;
char * hdr = " SPI " ;
if ( vcpu ) {
hdr = " VCPU " ;
id = vcpu - > vcpu_id ;
}
seq_printf ( s , " \n " ) ;
2018-07-16 15:06:21 +02:00
seq_printf ( s , " %s%2d TYP ID TGT_ID PLAEHCG HWID TARGET SRC PRI VCPU_ID \n " , hdr , id ) ;
seq_printf ( s , " ---------------------------------------------------------------- \n " ) ;
2017-01-17 23:09:13 +01:00
}
static void print_irq_state ( struct seq_file * s , struct vgic_irq * irq ,
struct kvm_vcpu * vcpu )
{
char * type ;
2020-03-04 20:33:30 +00:00
bool pending ;
2017-01-17 23:09:13 +01:00
if ( irq - > intid < VGIC_NR_SGIS )
type = " SGI " ;
else if ( irq - > intid < VGIC_NR_PRIVATE_IRQS )
type = " PPI " ;
2018-03-23 15:18:26 +00:00
else if ( irq - > intid < VGIC_MAX_SPI )
2017-01-17 23:09:13 +01:00
type = " SPI " ;
2018-03-23 15:18:26 +00:00
else
type = " LPI " ;
2017-01-17 23:09:13 +01:00
if ( irq - > intid = = 0 | | irq - > intid = = VGIC_NR_PRIVATE_IRQS )
print_header ( s , irq , vcpu ) ;
2020-03-04 20:33:30 +00:00
pending = irq - > pending_latch ;
if ( irq - > hw & & vgic_irq_is_sgi ( irq - > intid ) ) {
int err ;
err = irq_get_irqchip_state ( irq - > host_irq ,
IRQCHIP_STATE_PENDING ,
& pending ) ;
WARN_ON_ONCE ( err ) ;
}
2017-01-17 23:09:13 +01:00
seq_printf ( s , " %s %4d "
" %2d "
2018-07-16 15:06:21 +02:00
" %d%d%d%d%d%d%d "
2017-01-17 23:09:13 +01:00
" %8d "
" %8x "
" %2x "
" %3d "
" %2d "
" \n " ,
type , irq - > intid ,
( irq - > target_vcpu ) ? irq - > target_vcpu - > vcpu_id : - 1 ,
2020-03-04 20:33:30 +00:00
pending ,
2017-01-17 23:09:13 +01:00
irq - > line_level ,
irq - > active ,
irq - > enabled ,
irq - > hw ,
irq - > config = = VGIC_CONFIG_LEVEL ,
2018-07-16 15:06:21 +02:00
irq - > group ,
2017-01-17 23:09:13 +01:00
irq - > hwintid ,
irq - > mpidr ,
irq - > source ,
irq - > priority ,
( irq - > vcpu ) ? irq - > vcpu - > vcpu_id : - 1 ) ;
}
static int vgic_debug_show ( struct seq_file * s , void * v )
{
struct kvm * kvm = ( struct kvm * ) s - > private ;
struct vgic_state_iter * iter = ( struct vgic_state_iter * ) v ;
struct vgic_irq * irq ;
struct kvm_vcpu * vcpu = NULL ;
2018-05-11 15:20:12 +01:00
unsigned long flags ;
2017-01-17 23:09:13 +01:00
if ( iter - > dist_id = = 0 ) {
print_dist_state ( s , & kvm - > arch . vgic ) ;
return 0 ;
}
if ( ! kvm - > arch . vgic . initialized )
return 0 ;
2018-03-23 15:18:26 +00:00
if ( iter - > vcpu_id < iter - > nr_cpus )
2017-01-17 23:09:13 +01:00
vcpu = kvm_get_vcpu ( kvm , iter - > vcpu_id ) ;
2018-03-23 15:18:26 +00:00
irq = vgic_get_irq ( kvm , vcpu , iter - > intid ) ;
if ( ! irq ) {
seq_printf ( s , " LPI %4d freed \n " , iter - > intid ) ;
return 0 ;
2017-01-17 23:09:13 +01:00
}
2019-01-07 15:06:15 +00:00
raw_spin_lock_irqsave ( & irq - > irq_lock , flags ) ;
2017-01-17 23:09:13 +01:00
print_irq_state ( s , irq , vcpu ) ;
2019-01-07 15:06:15 +00:00
raw_spin_unlock_irqrestore ( & irq - > irq_lock , flags ) ;
2017-01-17 23:09:13 +01:00
2018-03-23 15:18:26 +00:00
vgic_put_irq ( kvm , irq ) ;
2017-01-17 23:09:13 +01:00
return 0 ;
}
2020-09-16 10:50:23 +08:00
static const struct seq_operations vgic_debug_sops = {
2017-01-17 23:09:13 +01:00
. start = vgic_debug_start ,
. next = vgic_debug_next ,
. stop = vgic_debug_stop ,
. show = vgic_debug_show
} ;
2020-09-16 10:50:23 +08:00
DEFINE_SEQ_ATTRIBUTE ( vgic_debug ) ;
2017-01-17 23:09:13 +01:00
2018-05-29 18:22:04 +02:00
void vgic_debug_init ( struct kvm * kvm )
2017-01-17 23:09:13 +01:00
{
2018-05-29 18:22:04 +02:00
debugfs_create_file ( " vgic-state " , 0444 , kvm - > debugfs_dentry , kvm ,
& vgic_debug_fops ) ;
2017-01-17 23:09:13 +01:00
}
2018-05-29 18:22:04 +02:00
void vgic_debug_destroy ( struct kvm * kvm )
2017-01-17 23:09:13 +01:00
{
}